mirror of
https://github.com/open-goal/jak-project
synced 2026-07-07 22:22:21 -04:00
[decompiler] Print floats as neatly as possible (#307)
* [decompiler] Print floats as neatly as possible * style fixes * more * Update test.bat * Add and fix tests * Jesus christ fine
This commit is contained in:
+34
-4
@@ -12,7 +12,7 @@
|
||||
* Object::make_<type>
|
||||
*
|
||||
* To convert an Object into a more specific object, use the as_<type> method of Object.
|
||||
* It will throw an exception is you get the type wrong.
|
||||
* It will throw an exception if you get the type wrong.
|
||||
*
|
||||
* These are all the types:
|
||||
*
|
||||
@@ -90,12 +90,42 @@ std::string fixed_to_string(FloatType x) {
|
||||
s64 rounded = x;
|
||||
bool exact_int = ((float)rounded) == x;
|
||||
|
||||
// it's an integer number, so let's just get this over with asap
|
||||
if (exact_int) {
|
||||
sprintf(buff, "%.1f", x);
|
||||
sprintf(buff, "%lld.0", rounded);
|
||||
return {buff};
|
||||
} else {
|
||||
sprintf(buff, "%.6f", x);
|
||||
return {buff};
|
||||
// not an integer - see how many decimal cases we need
|
||||
// i'm not sure what happens if x is a NaN/inf...
|
||||
|
||||
// buffer for format string
|
||||
char fmt_buf[256];
|
||||
|
||||
// we are going to try our hardest to make sure the output is re-parseable
|
||||
// for what it's worth, the lowest representable 32-bit floating point number has almost 50
|
||||
// decimal cases, although:
|
||||
// - by that point we should just be using scientific notation instead?
|
||||
// - the PS2 DOES NOT DENORMALIZE FLOATS, or handle NaNs/infs! so the representation wouldn't
|
||||
// be accurate anyway
|
||||
// - we might not ever encounter numbers like that. the pretty printer has a "banned" floats
|
||||
// list just in case
|
||||
|
||||
// 99 might seem high, but we are gonna get the result in under 10 or so most of the time, so
|
||||
// it's fine.
|
||||
// maybe there's some math principles or other tricks to optimize this?
|
||||
for (int i = 1; i <= 99; ++i) {
|
||||
// generate the format string
|
||||
sprintf(fmt_buf, "%%.%df", i);
|
||||
sprintf(buff, fmt_buf, x);
|
||||
|
||||
auto float_from_string = float(std::stod(buff));
|
||||
float value_as_float = float(x);
|
||||
bool are_they_equal = float_from_string == value_as_float;
|
||||
|
||||
if (are_they_equal)
|
||||
return {buff};
|
||||
}
|
||||
throw std::runtime_error("a float could not be represented accurately");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* Object::make_<type>
|
||||
*
|
||||
* To convert an Object into a more specific object, use the as_<type> method of Object.
|
||||
* It will throw an exception is you get the type wrong.
|
||||
* It will throw an exception if you get the type wrong.
|
||||
*
|
||||
* These are all the types:
|
||||
*
|
||||
|
||||
@@ -15,30 +15,19 @@
|
||||
namespace pretty_print {
|
||||
|
||||
namespace {
|
||||
const std::unordered_set<float> allowed_floats = {
|
||||
0.0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375, 0.5, 0.5625, 0.625,
|
||||
0.6875, 0.75, 0.8125, 0.875, 0.9375, -0.0625, -0.125, -0.1875, -0.25, -0.3125, -0.375,
|
||||
-0.4375, -0.5, -0.5625, -0.625, -0.6875, -0.75, -0.8125, -0.875, -0.9375};
|
||||
}
|
||||
// the integer representation is used here instead, wouldn't want really long numbers
|
||||
const std::unordered_set<u32> banned_floats = {};
|
||||
} // namespace
|
||||
/*!
|
||||
* Print a float in a nice representation if possibly, or an exact 32-bit integer constant to
|
||||
* be reinterpreted.
|
||||
*/
|
||||
goos::Object float_representation(float value) {
|
||||
s64 rounded = value;
|
||||
bool exact_int = ((float)rounded) == value;
|
||||
if (exact_int || allowed_floats.find(value) != allowed_floats.end()) {
|
||||
auto result = goos::Object::make_float(value);
|
||||
|
||||
// this is probably very slow for huge numbers of floats, but worth checking.
|
||||
auto float_as_string = result.print();
|
||||
auto as_float_again = float(std::stod(float_as_string));
|
||||
assert(as_float_again == value);
|
||||
|
||||
return result;
|
||||
u32 int_value;
|
||||
memcpy(&int_value, &value, 4);
|
||||
if (banned_floats.find(int_value) == banned_floats.end()) {
|
||||
return goos::Object::make_float(value);
|
||||
} else {
|
||||
u32 int_value;
|
||||
memcpy(&int_value, &value, 4);
|
||||
return pretty_print::build_list("the-as", "float", fmt::format("#x{:x}", int_value));
|
||||
}
|
||||
}
|
||||
@@ -632,4 +621,4 @@ goos::Object build_list(const std::vector<std::string>& symbols) {
|
||||
}
|
||||
return build_list(f.data(), f.size());
|
||||
}
|
||||
} // namespace pretty_print
|
||||
} // namespace pretty_print
|
||||
|
||||
@@ -32624,7 +32624,33 @@
|
||||
|
||||
|
||||
|
||||
|
||||
(define-extern *level-load-list* pair)
|
||||
(define-extern training level-load-info)
|
||||
(define-extern village1 level-load-info)
|
||||
(define-extern beach level-load-info)
|
||||
(define-extern jungle level-load-info)
|
||||
(define-extern jungleb level-load-info)
|
||||
(define-extern misty level-load-info)
|
||||
(define-extern firecanyon level-load-info)
|
||||
(define-extern village2 level-load-info)
|
||||
(define-extern sunken level-load-info)
|
||||
(define-extern sunkenb level-load-info)
|
||||
(define-extern swamp level-load-info)
|
||||
(define-extern rolling level-load-info)
|
||||
(define-extern ogre level-load-info)
|
||||
(define-extern village3 level-load-info)
|
||||
(define-extern snow level-load-info)
|
||||
(define-extern maincave level-load-info)
|
||||
(define-extern darkcave level-load-info)
|
||||
(define-extern robocave level-load-info)
|
||||
(define-extern lavatube level-load-info)
|
||||
(define-extern citadel level-load-info)
|
||||
(define-extern finalboss level-load-info)
|
||||
(define-extern intro level-load-info)
|
||||
(define-extern demo level-load-info)
|
||||
(define-extern title level-load-info)
|
||||
(define-extern halfpipe level-load-info)
|
||||
(define-extern default-level level-load-info)
|
||||
|
||||
;;(define-extern error object) ;; unknown type
|
||||
;;(define-extern details object) ;; unknown type
|
||||
@@ -33671,7 +33697,6 @@
|
||||
(define-extern ramdisk-sync function)
|
||||
;;(define-extern ramdisk-rpc-fill object) ;; unknown type
|
||||
(define-extern swap-sound-buffers function)
|
||||
;;(define-extern misty object) ;; unknown type
|
||||
(define-extern sound-group-stop function)
|
||||
(define-extern sound-set-ear-trans function)
|
||||
(define-extern sound-pause function)
|
||||
@@ -33687,7 +33712,6 @@
|
||||
;;(define-extern *flava-table* object) ;; unknown type
|
||||
(define-extern sound-basic-cb function)
|
||||
;;(define-extern sound-iop-info object) ;; unknown type
|
||||
;;(define-extern beach object) ;; unknown type
|
||||
;;(define-extern flava-table object) ;; unknown type
|
||||
;;(define-extern empty2 object) ;; unknown type
|
||||
(define-extern sound-bank-unload function)
|
||||
@@ -33695,31 +33719,20 @@
|
||||
(define-extern get-sound-buffer-entry function)
|
||||
(define-extern current-str-pos function)
|
||||
(define-extern sound-play-by-name function)
|
||||
;;(define-extern jungleb object) ;; unknown type
|
||||
;;(define-extern snow object) ;; unknown type
|
||||
;;(define-extern rolling object) ;; unknown type
|
||||
(define-extern sound-set-falloff-curve function)
|
||||
;;(define-extern jungle object) ;; unknown type
|
||||
(define-extern sound-name= function)
|
||||
(define-extern new-sound-id function)
|
||||
(define-extern sound-set-reverb function)
|
||||
(define-extern list-sounds function)
|
||||
(define-extern show-iop-info function)
|
||||
(define-extern sound-music-load function)
|
||||
;;(define-extern finalboss object) ;; unknown type
|
||||
(define-extern sound-trans-convert function)
|
||||
(define-extern sound-set-sound-falloff function)
|
||||
;;(define-extern flava-table-row object) ;; unknown type
|
||||
;;(define-extern swamp object) ;; unknown type
|
||||
;;(define-extern village1 object) ;; unknown type
|
||||
(define-extern sound-group-continue function)
|
||||
;;(define-extern village2 object) ;; unknown type
|
||||
(define-extern sound-continue function)
|
||||
;;(define-extern empty1 object) ;; unknown type
|
||||
;;(define-extern village3 object) ;; unknown type
|
||||
;;(define-extern *sound-player-enable* object) ;; unknown type
|
||||
;;(define-extern firecanyon object) ;; unknown type
|
||||
;;(define-extern citadel object) ;; unknown type
|
||||
(define-extern show-iop-memory function)
|
||||
(define-extern sound-set-volume function)
|
||||
(define-extern sound-command->string function)
|
||||
@@ -33729,11 +33742,8 @@
|
||||
;;(define-extern *ambient-spec* object) ;; unknown type
|
||||
(define-extern sound-angle-convert function)
|
||||
(define-extern ear-trans function)
|
||||
;;(define-extern ogre object) ;; unknown type
|
||||
;;(define-extern maincave object) ;; unknown type
|
||||
;;(define-extern credits object) ;; unknown type
|
||||
(define-extern sound-bank-load function)
|
||||
;;(define-extern lavatube object) ;; unknown type
|
||||
(define-extern sound-group-pause function)
|
||||
(define-extern current-str-id function)
|
||||
(define-extern sound-set-flava function)
|
||||
@@ -34332,7 +34342,6 @@
|
||||
(define-extern touch-tracker-init function)
|
||||
(define-extern position-in-front-of-camera! function)
|
||||
;;(define-extern reset object) ;; unknown type
|
||||
;;(define-extern *level-load-list* object) ;; unknown type
|
||||
;;(define-extern life object) ;; unknown type
|
||||
;;(define-extern *spawn-actors* object) ;; unknown type
|
||||
;;(define-extern eco-pill object) ;; unknown type
|
||||
@@ -34369,7 +34378,6 @@
|
||||
(define-extern progress-allowed? function)
|
||||
;;(define-extern *level-task-data* object) ;; unknown type
|
||||
;;(define-extern format-card object) ;; unknown type
|
||||
;;(define-extern training object) ;; unknown type
|
||||
;;(define-extern mc-format object) ;; unknown type
|
||||
(define-extern get-task-status function)
|
||||
;;(define-extern create-file object) ;; unknown type
|
||||
@@ -34613,7 +34621,6 @@
|
||||
(define-extern update-sky-tng-data function)
|
||||
;;(define-extern sky-base-polygons object) ;; unknown type
|
||||
(define-extern make-sky-textures function)
|
||||
;;(define-extern sunken object) ;; unknown type
|
||||
(define-extern sky-set-orbit function)
|
||||
(define-extern sky-set-sun-colors-sun function)
|
||||
(define-extern sky-set-sun-colors-halo function)
|
||||
@@ -34749,23 +34756,15 @@
|
||||
;;(define-extern vi2 object) ;; unknown type
|
||||
;;(define-extern rol object) ;; unknown type
|
||||
;;(define-extern cit object) ;; unknown type
|
||||
;;(define-extern darkcave object) ;; unknown type
|
||||
;;(define-extern bea object) ;; unknown type
|
||||
;;(define-extern vi3 object) ;; unknown type
|
||||
;;(define-extern fin object) ;; unknown type
|
||||
;;(define-extern display-no-wait object) ;; unknown type
|
||||
;;(define-extern sunkenb object) ;; unknown type
|
||||
;;(define-extern jun object) ;; unknown type
|
||||
;;(define-extern special-vis object) ;; unknown type
|
||||
;;(define-extern robocave object) ;; unknown type
|
||||
;;(define-extern mai object) ;; unknown type
|
||||
;;(define-extern lav object) ;; unknown type
|
||||
;;(define-extern sun object) ;; unknown type
|
||||
;;(define-extern title object) ;; unknown type
|
||||
;;(define-extern demo object) ;; unknown type
|
||||
;;(define-extern default-level object) ;; unknown type
|
||||
;;(define-extern intro object) ;; unknown type
|
||||
;;(define-extern halfpipe object) ;; unknown type
|
||||
;;(define-extern none object) ;; unknown type
|
||||
;;(define-extern robocave-vis object) ;; unknown type
|
||||
;;(define-extern tra object) ;; unknown type
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
out\build\Release\bin\goalc-test --gtest_filter="*TypeConsistency*"
|
||||
pause
|
||||
@@ -289,31 +289,31 @@ TEST_F(DataDecompTest, ContinuePoint) {
|
||||
" :name \"finalboss-start\"\n"
|
||||
" :level 'finalboss\n"
|
||||
" :trans (new 'static 'vector\n"
|
||||
" :x 11548456.000000\n"
|
||||
" :y 2215872.000000\n"
|
||||
" :z -19409498.000000\n"
|
||||
" :w 1.000000\n"
|
||||
" :x 11548456.0\n"
|
||||
" :y 2215872.0\n"
|
||||
" :z -19409498.0\n"
|
||||
" :w 1.0\n"
|
||||
" )\n"
|
||||
" :quat (new 'static 'vector\n"
|
||||
" :y (the-as float #x3f3b851f)\n"
|
||||
" :w (the-as float #x3f2e425b)\n"
|
||||
" :y 0.7325\n"
|
||||
" :w 0.6807\n"
|
||||
" )\n"
|
||||
" :camera-trans (new 'static 'vector\n"
|
||||
" :x 11513311.000000\n"
|
||||
" :y (the-as float #x4a0869de)\n"
|
||||
" :z -19435708.000000\n"
|
||||
" :w 1.000000\n"
|
||||
" :x 11513311.0\n"
|
||||
" :y 2234999.5\n"
|
||||
" :z -19435708.0\n"
|
||||
" :w 1.0\n"
|
||||
" )\n"
|
||||
" :camera-rot (new 'static 'array 'float 9\n"
|
||||
" (the-as float #x3f169ad4)\n"
|
||||
" 0.000000\n"
|
||||
" (the-as float #xbf4ef9db)\n"
|
||||
" (the-as float #x3ddbf488)\n"
|
||||
" (the-as float #x3f7db8bb)\n"
|
||||
" (the-as float #x3d9ff2e5)\n"
|
||||
" (the-as float #x3f4d288d)\n"
|
||||
" (the-as float #xbe07fcb9)\n"
|
||||
" (the-as float #x3f15460b)\n"
|
||||
" 0.5883\n"
|
||||
" 0.0\n"
|
||||
" -0.8085\n"
|
||||
" 0.1074\n"
|
||||
" 0.9911\n"
|
||||
" 0.0781\n"
|
||||
" 0.8014\n"
|
||||
" -0.1328\n"
|
||||
" 0.5831\n"
|
||||
" )\n"
|
||||
" :load-commands (('special \"citb-exit-plat-4\" #t))\n"
|
||||
" :vis-nick 'fin\n"
|
||||
@@ -341,4 +341,4 @@ TEST_F(DataDecompTest, FloatArray) {
|
||||
check_forms_equal(decomp.print(),
|
||||
"(new 'static 'array 'float 7\n"
|
||||
"1.0 0.0 1.0 0.0 1.0 0.0 1.0)");
|
||||
}
|
||||
}
|
||||
|
||||
+22
-12
@@ -155,11 +155,11 @@ TEST(GoosBuiltins, Addition) {
|
||||
|
||||
// two element adding
|
||||
EXPECT_EQ(e(i, "(+ 1 2)"), "3");
|
||||
EXPECT_EQ(e(i, "(+ 1.1 2.2)"), "3.300000");
|
||||
EXPECT_EQ(e(i, "(+ 1.1 2.2)"), "3.3");
|
||||
|
||||
// mixed
|
||||
EXPECT_EQ(e(i, "(+ 1 1.1)"), "2");
|
||||
EXPECT_EQ(e(i, "(+ 1.1 1)"), "2.100000");
|
||||
EXPECT_EQ(e(i, "(+ 1.1 1)"), "2.1");
|
||||
|
||||
// many, and check rounding happens at the right time
|
||||
EXPECT_EQ(e(i, "(+ 1 1.4 1.4 1.4 1.4)"), "5");
|
||||
@@ -178,11 +178,11 @@ TEST(GoosBuiltins, Multiplication) {
|
||||
|
||||
// two element adding
|
||||
EXPECT_EQ(e(i, "(* 3 2)"), "6");
|
||||
EXPECT_EQ(e(i, "(* 1.1 2.2)"), "2.420000");
|
||||
EXPECT_EQ(e(i, "(* 1.1 2.2)"), "2.42");
|
||||
|
||||
// mixed
|
||||
EXPECT_EQ(e(i, "(* 1 1.1)"), "1");
|
||||
EXPECT_EQ(e(i, "(* 1.1 1)"), "1.100000");
|
||||
EXPECT_EQ(e(i, "(* 1.1 1)"), "1.1");
|
||||
|
||||
// many, and check rounding happens at the right time
|
||||
EXPECT_EQ(e(i, "(* 3 1.4 1.4 1.4 1.4)"), "3");
|
||||
@@ -201,11 +201,11 @@ TEST(GoosBuiltins, Subtraction) {
|
||||
|
||||
// two element adding
|
||||
EXPECT_EQ(e(i, "(- 3 2)"), "1");
|
||||
EXPECT_EQ(e(i, "(- 1.1 2.2)"), "-1.100000");
|
||||
EXPECT_EQ(e(i, "(- 1.1 2.2)"), "-1.1");
|
||||
|
||||
// mixed
|
||||
EXPECT_EQ(e(i, "(- 1 1.1)"), "0");
|
||||
EXPECT_EQ(e(i, "(- 1.1 1)"), "0.100000");
|
||||
EXPECT_EQ(e(i, "(- 1.1 1)"), "0.1");
|
||||
|
||||
// many, and check rounding happens at the right time
|
||||
EXPECT_EQ(e(i, "(- 3 1.4 1.4 1.4 1.4)"), "-1");
|
||||
@@ -221,11 +221,11 @@ TEST(GoosBuiltins, Division) {
|
||||
|
||||
// two element adding
|
||||
EXPECT_EQ(e(i, "(/ 16 2)"), "8");
|
||||
EXPECT_EQ(e(i, "(/ 9. 2.)"), "4.500000");
|
||||
EXPECT_EQ(e(i, "(/ 9. 2.)"), "4.5");
|
||||
|
||||
// mixed
|
||||
EXPECT_EQ(e(i, "(/ 3 2.)"), "1");
|
||||
EXPECT_EQ(e(i, "(/ 3. 2)"), "1.500000");
|
||||
EXPECT_EQ(e(i, "(/ 3. 2)"), "1.5");
|
||||
|
||||
i.disable_printfs();
|
||||
for (auto x : {"(/ 1)", "(/ 1.0)", "(/)", "(/ 'a)", "(/ #\\a)", "(/ 1 :test 2)",
|
||||
@@ -494,7 +494,7 @@ TEST(GoosEval, EvalSelfEvaluating) {
|
||||
EXPECT_EQ(e(i, "010"), "10");
|
||||
EXPECT_EQ(e(i, "-010"), "-10");
|
||||
EXPECT_EQ(e(i, "\"test\""), "\"test\"");
|
||||
EXPECT_EQ(e(i, "1.2"), "1.200000"); // this depends on how we decide to print floats
|
||||
EXPECT_EQ(e(i, "1.2"), "1.2"); // this depends on how we decide to print floats
|
||||
EXPECT_EQ(e(i, "#\\a"), "#\\a");
|
||||
EXPECT_EQ(e(i, "#\\\\n"), "#\\\\n");
|
||||
|
||||
@@ -502,6 +502,16 @@ TEST(GoosEval, EvalSelfEvaluating) {
|
||||
EXPECT_ANY_THROW(e(i, "#\\\\a"));
|
||||
}
|
||||
|
||||
TEST(GoosEval, FloatEvalAndPrinting) {
|
||||
Interpreter i;
|
||||
EXPECT_EQ(e(i, "0.9999979734420776"), "0.999998");
|
||||
EXPECT_EQ(e(i, "0.999998"), e(i, "0.9999979734420776"));
|
||||
EXPECT_EQ(e(i, "1."), "1.0");
|
||||
EXPECT_EQ(e(i, ".03"), "0.03");
|
||||
EXPECT_EQ(e(i, "0.02999999932944774627685546875"), e(i, "0.03"));
|
||||
EXPECT_EQ(e(i, "0.5883"), e(i, "0.5882999897"));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Check named "keyword" arguments
|
||||
*/
|
||||
@@ -1020,8 +1030,8 @@ TEST(GoosObject, Float) {
|
||||
EXPECT_TRUE(different == same);
|
||||
|
||||
// check print and inspect
|
||||
EXPECT_EQ(different.print(), "-12.340000");
|
||||
EXPECT_EQ(different.inspect(), "[float] -12.340000\n");
|
||||
EXPECT_EQ(different.print(), "-12.34");
|
||||
EXPECT_EQ(different.inspect(), "[float] -12.34\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1300,4 +1310,4 @@ TEST(GoosBuiltins, Format) {
|
||||
TEST(GoosBuiltins, Error) {
|
||||
Interpreter i;
|
||||
EXPECT_ANY_THROW(e(i, "(error \"hi\")"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user