Closed ryhor-spivak closed 8 months ago
I still don't know what is wrong with my fix. You can just loop through all bit patterns of f16 and f32 and check it works as expected:
package main
import "core:fmt"
import "core:math"
@(require_results)
round_f16_fix :: proc "contextless" (x: f16) -> f16 {
return math.ceil(x - 0.4998) if x < 0 else math.floor(x + 0.4998)
}
@(require_results)
round_f32_fix :: proc "contextless" (x: f32) -> f32 {
return math.ceil(x - 0.49999997) if x < 0 else math.floor(x + 0.49999997)
}
@(require_results)
round_f64_fix :: proc "contextless" (x: f64) -> f64 {
return math.ceil(x - 0.49999999999999994) if x < 0 else math.floor(x + 0.49999999999999994)
}
round_fix :: proc{
round_f16_fix,
round_f32_fix,
round_f64_fix,
}
round_check :: proc(x: $FP)
{
rx := round_fix(x)
//rx := math.round(x) // this will show all bad cases of current math.round version
if math.is_nan(x) && !math.is_nan(rx)
{
fmt.printf("fail on %.9f(%h): rounds to %f(%h)\n", x, x, rx, rx )
return
}
f := math.floor(x)
c := math.ceil(x)
if x > 0
{
if x - f < c - x
{
if rx != f do fmt.printf("fail on %.9f(%h): rounds to %f(%h)\n", x, x, rx, rx )
}
else
{
if rx != c do fmt.printf("fail on %.9f(%h): rounds to %f(%h)\n", x, x, rx, rx )
}
}
else
{
if x - f <= c - x
{
if rx != f do fmt.printf("fail on %.9f(%h): rounds to %f(%h)\n", x, x, rx, rx )
}
else
{
if rx != c do fmt.printf("fail on %.9f(%h): rounds to %f(%h)\n", x, x, rx, rx )
}
}
}
main :: proc()
{
fmt.printf("checking all f16:\n")
{
u : u16 = 0
for
{
round_check(transmute(f16)u)
u += 1
if u == 0 do break
}
}
fmt.printf("checking all f32:\n")
{
u : u32 = 0
for
{
round_check(transmute(f32)u)
u += 1
if u == 0 do break
}
}
}
round(-0.0)
should return -0.0
, also I don't know if you can rely on 0.5 + 0.499... == 1
-0 can be handled by
@(require_results)
round_f16_fix :: proc "contextless" (x: f16) -> f16 {
return math.copy_sign(math.floor(abs(x) + 0.4998), x)
}
@(require_results)
round_f32_fix :: proc "contextless" (x: f32) -> f32 {
return math.copy_sign(math.floor(abs(x) + 0.49999997), x)
}
@(require_results)
round_f64_fix :: proc "contextless" (x: f64) -> f64 {
return math.copy_sign(math.floor(abs(x) + 0.49999999999999994), x)
}
And this probably will be faster too.
For relying on fp addition: in all other places we already rely on it rounding to nearest representable float with ties to even.
Context
Expected Behavior
I expect this program:
to produce this output:
Current Behavior
But currently, it produces this:
My attempt to fix this (https://github.com/odin-lang/Odin/pull/2675) was closed as incorrect, so this should be fixed is some other way.