Skip to content

strconv: incorrect rounding to nearest even #21714

@griesemer

Description

@griesemer

The following statements

fmt.Printf("x = %.1f\n", 0.35)
fmt.Printf("x = %.1f\n", 0.45)
fmt.Printf("x = %.1f\n", 0.55)
fmt.Printf("x = %.1f\n", 0.65)

produce

x = 0.3
x = 0.5
x = 0.6
x = 0.7

(https://play.golang.org/p/kLC7Ap0cpP).

However, assuming rounding to nearest even, one would expect the result to be

x = 0.4
x = 0.4
x = 0.6
x = 0.6

(The examples use fmt.Printf but the culprit is in the underlying strconv code. The documentation doesn't explicitly state that it rounds to nearest even, but that is definitively the intent of the implementation which contains code and comments to that effect.).

The problem of course is that these numbers cannot be accurately represented in binary form and when it comes to the rounding decision, what should be .5 is sometimes below or above that value.

That said, the correct result could in fact be obtained if rounding were done on the shortest decimal representation that produces the incoming number, that is if rounding where done on the output of using the "%g" format (or strconv.FormatFloat with precision -1). Unfortunately, computing that representation is much more expensive than what happens now.

We can probably not fix this. For one, it's been like this forever. Also, a corresponding C program

#include <stdio.h>

int main() {
	printf("x = %.1f\n", 0.35);
	printf("x = %.1f\n", 0.45);
	printf("x = %.1f\n", 0.55);
	printf("x = %.1f\n", 0.65);
	return 0;
}

produces the same (incorrect) result. And so does math/big.Float formatting with the same arguments (since the code mirrors the strconv code).

But we should probably document it and perhaps even provide a "correct" routine for cases where this truly matters.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DocumentationIssues describing a change to documentation.FrozenDueToAgeNeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions