odin-lang/Odin

strconv.parse_f64_prefix() returns incorrect values for negative numbers

Closed this issue · 2 comments

Context

  • Operating System & Odin Version:
    Odin: dev-2024-10:9f609dd74
    OS: openSUSE Tumbleweed, Linux 6.11.0-1-default
    CPU: AMD Ryzen 9 5900X 12-Core Processor
    RAM: 31989 MiB
    Backend: LLVM 18.1.8

Expected Behavior

When running the following Odin program, using odin run .:

package strconv_test

import "core:strconv"
import "core:fmt"

test :: proc(str: string) {
  val, num, ok := strconv.parse_f64_prefix(str)
  fmt.println(str)
  fmt.println("->", val, num, ok)
}

main :: proc() {
  test("80")
  test("160")
  test("-80")
  test("-160")
}

I expect the output to be:

80
-> 80 2 true
160
-> 160 3 true
-80
-> -80 3 true
-160
-> -160 4 true

Current Behavior

Instead I get:

80
-> 80 2 true
160
-> 160 3 true
-80
-> -79.86500349943526 3 true
-160
-> -159.73000699887052 4 true

I understand floating point numbers are not 100% accurate but for negative numbers that seems to be very wrong

That would indeed be very wrong. Especially for f64, integers with abs(x) < (1 << 54) are supposed to be representable without truncating.

In core/strconv/strconv.odin

		f := f64(mantissa)
		if neg {
			f = -f
		}
		switch {
		case exp == 0:
			return f, nr, true
		case exp > 0 && exp <= 15+22:
			if exp > 22 {
				f *= pow10[exp-22]
				exp = 22
			}
			if f > 1e15 || f < 1e-15 {
				break trunc_block
			}
			return f * pow10[exp], nr, true
		case -22 <= exp && exp < 0:
			return f / pow10[-exp], nr, true
		}

If neg is true, then f < 0, so f < 1e-15, so it's always breaking on trunc_block when it was intended to return f * pow10[exp].