Bug 53586

Summary: Rounding errors in converting cm to px
Product: WebKit Reporter: Berend-Jan Wever <skylined>
Component: CSSAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: eric, hyatt, jamesr, weizhengshen, yevseytsev
Priority: P1    
Version: 528+ (Nightly build)   
Hardware: PC   
OS: Windows Vista   
Attachments:
Description Flags
Repro none

Description Berend-Jan Wever 2011-02-02 04:55:19 PST
Created attachment 80910 [details]
Repro

It appears webkit is rounding sizes specified in "cm" down, where other browsers are not.
Repro: run the following test in multiple browsers:
<!doctype html>
<script>
  for (var i = 0; i < 5; i++) {
    var iSize = Math.pow(10, i);
    var o = document.createElement('span');
    document.documentElement.appendChild(o);
    o.style.display = 'block';
    o.style.width = iSize + 'cm';
    document.write('1cm = ' + (o.clientWidth / iSize) + 'px +/- ~0.' + new Array(i).join('0') + '5<br/>');
  }
</script>
Chrome/Safari both output the same values, with rounding down errors:
1cm = 37px +/- ~0.5
1cm = 37.7px +/- ~0.05
1cm = 37.79px +/- ~0.005
1cm = 37.795px +/- ~0.0005
1cm = 37.7952px +/- ~0.00005

Firefox/MSIE both output the same values, which seem to get rounded correctly:
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.8px +/- ~0.005
1cm = 37.795px +/- ~0.0005
1cm = 37.7953px +/- ~0.00005

Opera seems to agree with FF/MSIE for the most part, but doesn't appear to limit sizes to 32,767 (0x7FFF), so the last two values are not calculated correctly:
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.8px +/- ~0.005
1cm = 32.767px +/- ~0.0005
1cm = 3.2767px +/- ~0.00005
Comment 1 Sanders Shen 2012-02-24 21:29:10 PST
I think this issue is caused by the implementation of "roundForImpreciseConversion"(Source/WebCore/css/CSSPrimitiveValue.h).
It uses "static_cast" to convert a double number to other type, 
then it would case the precision losing.

I modify the logic here as followings:
return ((value > max) || (value < min)) ? 0 : static_cast<T>(lround(value));
Then, the Qt porting could get same result with Firefox/MSIE.

But, I am not sure whether it has any other side effects.

Anyone could explain why we just use "static_cast" here?
Comment 2 yevseytsev 2019-02-08 10:54:53 PST
Hello, I decided to start my contributions to WebKit from some bug triaging, and found out this bug. After reproducing the bug's code (Feb 2019) I actually got the following results for different browsers:

SAFARI
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.795px +/- ~0.005
1cm = 37.7953px +/- ~0.0005

FIREFOX
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.795px +/- ~0.005
1cm = 37.7953px +/- ~0.0005

GOOGLE CHROME
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.795px +/- ~0.005
1cm = 37.7953px +/- ~0.0005

OPERA
1cm = 38px +/- ~0.5
1cm = 37.8px +/- ~0.5
1cm = 37.8px +/- ~0.05
1cm = 37.795px +/- ~0.005
1cm = 37.7953px +/- ~0.0005

All the values are the same for all 4 browsers, which means that the bug was fixed.
After researching the implementation of "roundForImpreciseConversion" function in Source/WebCore/css/CSSPrimitiveValue.h file I found out the following code:

template<typename T> inline T roundForImpreciseConversion(double value)
{
    // Dimension calculations are imprecise, often resulting in values of e.g.
    // 44.99998.  We need to go ahead and round if we're really close to the
    // next integer value.
    value += (value < 0) ? -0.01 : +0.01;
    return ((value > std::numeric_limits<T>::max()) || (value < std::numeric_limits<T>::min())) ? 0 : static_cast<T>(value);
}

I believe that after Sanders Shen's (weizhengshen@gmail.com) edit (24 Feb, 2012), macpherson@chromium.org did the final edit to "return" function code to fix this bug (20 Mar, 2012).

I think this bug is no longer a bug, because any of that 4 browsers does not have any rounding errors. 

As well we can't talk about WebKit bugs anymore in context of Chrome(desktop) and Opera, because in April 2013, Google and Opera Software announced the transition to the Blink engine. WebKit powers a Google Chrome only on iOS version of the browser.