Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/scale/Log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,13 @@ class LogScale extends IntervalScale {

return zrUtil.map(ticks, function (tick) {
const val = tick.value;
let powVal = fixRound(mathPow(base, val));
const rawVal = mathPow(base, val);
let roundingCriterion = null;

// Fix #21099
const precision = numberUtil.getPrecisionSafe(rawVal) || 0;
let powVal = parseFloat(fixRound(rawVal, precision as number, true));
Copy link
Copy Markdown
Member

@100pah 100pah Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SihongShen @Ovilia I don't fully understand the purpose of this handling.

If rawVal has the fractional part digits <= 20, rawVal will be equal to powVal, nothing changes.
Otherwise, if the fractional part digits > 20, the number.round method internally clamp it to 20, but that doesn't make sense in this scenario and may lead incorrect results, e.g, (1e-40).toFixed(20) we get 0.

image

From my understanding, the "fix round" applied in src/scale is meant to correct rounding errors introduced by floating-point computation, (such as the well-known case where 0.1 + 0.2 === 0.30000000000000004, when representing and calculating decimal fractional number in binary).

But Math.pow(base, tick.value) shouldn't suffer from such kind of rounding error, because the Log.ts guarantees interval and tick.value to be integers, and the base is typically an integer as well. They can be exactly represented in ieee754 64-bit floating-point number (assume no overflow - this is an separate topic).

Therefore, there is no opportunity for rounding errors to be accumulated beyond Number.EPSILON * Math.pow(2, ieee754_double_exponent(tick.value)) and result in a different result when converted back to a decimal string.
(Correct me if I'me wrong.)

Therefore, I think we should just remove this precision and rounding handling here. Just be:

let powVal = mathPow(base, val);

I tried to fix it in #21120


// Fix #4158
if (val === extent[0] && this._fixMin) {
roundingCriterion = originalExtent[0];
Expand Down Expand Up @@ -202,7 +206,7 @@ class LogScale extends IntervalScale {
if (!scaleBreakHelper) {
return;
}
const {parsedOriginal, parsedLogged} = scaleBreakHelper.logarithmicParseBreaksFromOption(
const { parsedOriginal, parsedLogged } = scaleBreakHelper.logarithmicParseBreaksFromOption(
breakOptionList,
this.base,
zrUtil.bind(this.parse, this)
Expand Down
61 changes: 61 additions & 0 deletions test/logScale.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading