diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-08-02 15:09:30 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-08-02 15:09:30 -0300 |
| commit | 1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a (patch) | |
| tree | 5162b1f662c4dcb6127ba03866096f70d0c298ab /testes | |
| parent | 4c6afbcb01d1cae72d829af5301df5f592fa2079 (diff) | |
| download | lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.tar.gz lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.tar.bz2 lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.zip | |
Floats formatted with "correct" precision
Conversion float->string ensures that, for any float f,
tonumber(tostring(f)) == f, but still avoiding noise like 1.1
converting to "1.1000000000000001".
Diffstat (limited to 'testes')
| -rw-r--r-- | testes/math.lua | 106 |
1 files changed, 103 insertions, 3 deletions
diff --git a/testes/math.lua b/testes/math.lua index 0191f7dd..3937b9ce 100644 --- a/testes/math.lua +++ b/testes/math.lua | |||
| @@ -22,6 +22,18 @@ do | |||
| 22 | end | 22 | end |
| 23 | end | 23 | end |
| 24 | 24 | ||
| 25 | |||
| 26 | -- maximum exponent for a floating-point number | ||
| 27 | local maxexp = 0 | ||
| 28 | do | ||
| 29 | local p = 2.0 | ||
| 30 | while p < math.huge do | ||
| 31 | maxexp = maxexp + 1 | ||
| 32 | p = p + p | ||
| 33 | end | ||
| 34 | end | ||
| 35 | |||
| 36 | |||
| 25 | local function isNaN (x) | 37 | local function isNaN (x) |
| 26 | return (x ~= x) | 38 | return (x ~= x) |
| 27 | end | 39 | end |
| @@ -34,8 +46,8 @@ do | |||
| 34 | local x = 2.0^floatbits | 46 | local x = 2.0^floatbits |
| 35 | assert(x > x - 1.0 and x == x + 1.0) | 47 | assert(x > x - 1.0 and x == x + 1.0) |
| 36 | 48 | ||
| 37 | print(string.format("%d-bit integers, %d-bit (mantissa) floats", | 49 | local msg = " %d-bit integers, %d-bit*2^%d floats" |
| 38 | intbits, floatbits)) | 50 | print(string.format(msg, intbits, floatbits, maxexp)) |
| 39 | end | 51 | end |
| 40 | 52 | ||
| 41 | assert(math.type(0) == "integer" and math.type(0.0) == "float" | 53 | assert(math.type(0) == "integer" and math.type(0.0) == "float" |
| @@ -803,7 +815,11 @@ do | |||
| 803 | end | 815 | end |
| 804 | 816 | ||
| 805 | 817 | ||
| 806 | print("testing 'math.random'") | 818 | -- |
| 819 | -- [[================================================================== | ||
| 820 | print("testing 'math.random'") | ||
| 821 | -- -=================================================================== | ||
| 822 | -- | ||
| 807 | 823 | ||
| 808 | local random, max, min = math.random, math.max, math.min | 824 | local random, max, min = math.random, math.max, math.min |
| 809 | 825 | ||
| @@ -1019,6 +1035,90 @@ assert(not pcall(random, minint + 1, minint)) | |||
| 1019 | assert(not pcall(random, maxint, maxint - 1)) | 1035 | assert(not pcall(random, maxint, maxint - 1)) |
| 1020 | assert(not pcall(random, maxint, minint)) | 1036 | assert(not pcall(random, maxint, minint)) |
| 1021 | 1037 | ||
| 1038 | -- ]]================================================================== | ||
| 1039 | |||
| 1040 | |||
| 1041 | -- | ||
| 1042 | -- [[================================================================== | ||
| 1043 | print("testing precision of 'tostring'") | ||
| 1044 | -- -=================================================================== | ||
| 1045 | -- | ||
| 1046 | |||
| 1047 | -- number of decimal digits supported by float precision | ||
| 1048 | local decdig = math.floor(floatbits * math.log(2, 10)) | ||
| 1049 | print(string.format(" %d-digit float numbers with full precision", | ||
| 1050 | decdig)) | ||
| 1051 | -- number of decimal digits supported by integer precision | ||
| 1052 | local Idecdig = math.floor(math.log(maxint, 10)) | ||
| 1053 | print(string.format(" %d-digit integer numbers with full precision", | ||
| 1054 | Idecdig)) | ||
| 1055 | |||
| 1056 | do | ||
| 1057 | -- Any number should print so that reading it back gives itself: | ||
| 1058 | -- tonumber(tostring(x)) == x | ||
| 1059 | |||
| 1060 | -- Mersenne fractions | ||
| 1061 | local p = 1.0 | ||
| 1062 | for i = 1, maxexp do | ||
| 1063 | p = p + p | ||
| 1064 | local x = 1 / (p - 1) | ||
| 1065 | assert(x == tonumber(tostring(x))) | ||
| 1066 | end | ||
| 1067 | |||
| 1068 | -- some random numbers in [0,1) | ||
| 1069 | for i = 1, 100 do | ||
| 1070 | local x = math.random() | ||
| 1071 | assert(x == tonumber(tostring(x))) | ||
| 1072 | end | ||
| 1073 | |||
| 1074 | -- different numbers shold print differently. | ||
| 1075 | -- check pairs of floats with minimum detectable difference | ||
| 1076 | local p = floatbits - 1 | ||
| 1077 | for i = 1, maxexp - 1 do | ||
| 1078 | for _, i in ipairs{-i, i} do | ||
| 1079 | local x = 2^i | ||
| 1080 | local diff = 2^(i - p) -- least significant bit for 'x' | ||
| 1081 | local y = x + diff | ||
| 1082 | local fy = tostring(y) | ||
| 1083 | assert(x ~= y and tostring(x) ~= fy) | ||
| 1084 | assert(tonumber(fy) == y) | ||
| 1085 | end | ||
| 1086 | end | ||
| 1087 | |||
| 1088 | |||
| 1089 | -- "reasonable" numerals should be printed like themselves | ||
| 1090 | |||
| 1091 | -- create random float numerals with 5 digits, with a decimal point | ||
| 1092 | -- inserted in all places. (With more than 5, things like "0.00001" | ||
| 1093 | -- reformats like "1e-5".) | ||
| 1094 | for i = 1, 1000 do | ||
| 1095 | -- random numeral with 5 digits | ||
| 1096 | local x = string.format("%.5d", math.random(0, 99999)) | ||
| 1097 | for i = 2, #x do | ||
| 1098 | -- insert decimal point at position 'i' | ||
| 1099 | local y = string.sub(x, 1, i - 1) .. "." .. string.sub(x, i, -1) | ||
| 1100 | y = string.gsub(y, "^0*(%d.-%d)0*$", "%1") -- trim extra zeros | ||
| 1101 | assert(y == tostring(tonumber(y))) | ||
| 1102 | end | ||
| 1103 | end | ||
| 1104 | |||
| 1105 | -- all-random floats | ||
| 1106 | local Fsz = string.packsize("n") -- size of floats in bytes | ||
| 1107 | |||
| 1108 | for i = 1, 400 do | ||
| 1109 | local s = string.pack("j", math.random(0)) -- a random string of bits | ||
| 1110 | while #s < Fsz do -- make 's' long enough | ||
| 1111 | s = s .. string.pack("j", math.random(0)) | ||
| 1112 | end | ||
| 1113 | local n = string.unpack("n", s) -- read 's' as a float | ||
| 1114 | s = tostring(n) | ||
| 1115 | if string.find(s, "^%-?%d") then -- avoid NaN, inf, -inf | ||
| 1116 | assert(tonumber(s) == n) | ||
| 1117 | end | ||
| 1118 | end | ||
| 1119 | |||
| 1120 | end | ||
| 1121 | -- ]]================================================================== | ||
| 1022 | 1122 | ||
| 1023 | 1123 | ||
| 1024 | print('OK') | 1124 | print('OK') |
