Bug 86065 - FractionalLayoutUnit minor math bugs
Summary: FractionalLayoutUnit minor math bugs
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Allan Sandfeld Jensen
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-05-10 01:04 PDT by Allan Sandfeld Jensen
Modified: 2012-07-31 15:41 PDT (History)
6 users (show)

See Also:


Attachments
Patch (4.59 KB, patch)
2012-05-10 01:13 PDT, Allan Sandfeld Jensen
no flags Details | Formatted Diff | Diff
Patch (4.57 KB, patch)
2012-05-11 00:49 PDT, Allan Sandfeld Jensen
darin: review-
Details | Formatted Diff | Diff
Patch (4.79 KB, patch)
2012-05-21 10:04 PDT, Allan Sandfeld Jensen
no flags Details | Formatted Diff | Diff
Patch (3.62 KB, patch)
2012-07-11 06:23 PDT, Allan Sandfeld Jensen
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Allan Sandfeld Jensen 2012-05-10 01:04:05 PDT
There a few cases with the new FractionalUnit types where mathematical operations are performed on the wrong type or has not taken precedence rules into account.
Comment 1 Allan Sandfeld Jensen 2012-05-10 01:13:55 PDT
Created attachment 141105 [details]
Patch
Comment 2 Levi Weintraub 2012-05-10 10:26:44 PDT
Comment on attachment 141105 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=141105&action=review

Some good catches :)

> Source/WebCore/ChangeLog:3
> +        FractionalUnit minor math bugs

FractionalLayoutUnit :)

> Source/WebCore/platform/FractionalLayoutUnit.h:572
> +    a = a - b.toFloat();

Is this actually a change in behavior?

> Source/WebCore/platform/FractionalLayoutUnit.h:586
> +    float value = a.toFloat() * b;
> +    a = value;
> +    return a;

Why would we want to do something other than the standard overload of float * FractionalLayoutUnit? Why not build this out of the already-defined operators?
Comment 3 Allan Sandfeld Jensen 2012-05-10 11:28:06 PDT
(In reply to comment #2)
> (From update of attachment 141105 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=141105&action=review
> 
> Some good catches :)
> 
> > Source/WebCore/ChangeLog:3
> > +        FractionalUnit minor math bugs
> 
> FractionalLayoutUnit :)
> 
Right :D

> > Source/WebCore/platform/FractionalLayoutUnit.h:572
> > +    a = a - b.toFloat();
> 
> Is this actually a change in behavior?
> 
No.  

> > Source/WebCore/platform/FractionalLayoutUnit.h:586
> > +    float value = a.toFloat() * b;
> > +    a = value;
> > +    return a;
> 
> Why would we want to do something other than the standard overload of float * FractionalLayoutUnit? Why not build this out of the already-defined operators?

To avoid the float being cast to fractionallayoutunit before the multiplication, I found this after experimenting with setting the FractionalLayoutUnit(float) constructor to explicit, specifically to see if any floats were converted this way losing precision. It turned out setting the constructor to explicit is not a good idea because it is used implicitly all over the place, but I did find a few places it shouldn't have been used.
Comment 4 Allan Sandfeld Jensen 2012-05-11 00:49:57 PDT
Created attachment 141345 [details]
Patch
Comment 5 Darin Adler 2012-05-12 08:48:04 PDT
Comment on attachment 141345 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=141345&action=review

Some good changes here, but some others that I believe are not helpful.

> Source/WebCore/platform/FractionalLayoutUnit.h:152
> -    static float epsilon() { return 1 / kFixedPointDenominator; }
> +    static float epsilon() { return 1. / kFixedPointDenominator; }

The comment says this should use “float division”, but this will use double-precision division and then round to float. That’s probably OK because it happens at compile time.

> Source/WebCore/platform/FractionalLayoutUnit.h:348
> -    long long rawVal = static_cast<long long>(a.rawValue()) * b.rawValue() / kFixedPointDenominator;
> +    long long rawVal = (static_cast<long long>(a.rawValue()) * b.rawValue()) / kFixedPointDenominator;

Given my understanding of the C rules for operators and parentheses and order of operations, I believe adding these parentheses has no effect. Please do not add them unless you have evidence I am wrong.

> Source/WebCore/platform/FractionalLayoutUnit.h:416
> -    long long rawVal = static_cast<long long>(kFixedPointDenominator) * a.rawValue() / b.rawValue();
> +    long long rawVal = (static_cast<long long>(kFixedPointDenominator) * a.rawValue()) / b.rawValue();

Ditto.

> Source/WebCore/platform/FractionalLayoutUnit.h:572
> -    a = a - b;
> +    a = a - b.toFloat();

I don’t think this change adds value.

> Source/WebCore/platform/FractionalLayoutUnit.h:584
> +    a = a.toFloat() * b;

Why does the explicit cast here yield higher precision? It should not. Please do not make this change unless it’s actually helpful.

> Source/WebCore/platform/FractionalLayoutUnit.h:590
> +    a = a * b.toFloat();

The explicit call to toFloat here should not be needed.
Comment 6 Darin Adler 2012-05-12 08:50:31 PDT
Comment on attachment 141345 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=141345&action=review

>> Source/WebCore/platform/FractionalLayoutUnit.h:572
>> +    a = a - b.toFloat();
> 
> I don’t think this change adds value.

OK, I guess I was wrong about this one. It seems that without the explicit conversion the math is done by converting the float to a FractionLayoutUnit. I think that it’s nice to fix it, but we need this fixed for the plain old operator-, not just for operator-=. We have a design problem where when we do math with a float and a FractionalLayoutUnit it is getting done as FractionalLayoutUnit math.

>> Source/WebCore/platform/FractionalLayoutUnit.h:584
>> +    a = a.toFloat() * b;
> 
> Why does the explicit cast here yield higher precision? It should not. Please do not make this change unless it’s actually helpful.

Same comment applies. I was wrong. But fixing this in *= is not sufficient. We need the * operator to work properly too.

>> Source/WebCore/platform/FractionalLayoutUnit.h:590
>> +    a = a * b.toFloat();
> 
> The explicit call to toFloat here should not be needed.

Same comment applies. I was wrong. But fixing this in *= is not sufficient. We need the * operator to work properly too.
Comment 7 Allan Sandfeld Jensen 2012-05-12 09:54:21 PDT
(In reply to comment #5)
> (From update of attachment 141345 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=141345&action=review
> 
> Some good changes here, but some others that I believe are not helpful.
> 
> > Source/WebCore/platform/FractionalLayoutUnit.h:348
> > -    long long rawVal = static_cast<long long>(a.rawValue()) * b.rawValue() / kFixedPointDenominator;
> > +    long long rawVal = (static_cast<long long>(a.rawValue()) * b.rawValue()) / kFixedPointDenominator;
> 
> Given my understanding of the C rules for operators and parentheses and order of operations, I believe adding these parentheses has no effect. Please do not add them unless you have evidence I am wrong.
> 
Division in mathematical expressions has higher precedence than multiplications so A * B / C is calculated A * (B / C) in this case no only dividing in int instead of long long, but losing precision due to integer division before multiplication.  

Though to admit I am assuming C is using standard precedence rules here.

> > Source/WebCore/platform/FractionalLayoutUnit.h:572
> > -    a = a - b;
> > +    a = a - b.toFloat();
> I don’t think this change adds value.

Coding style. The problem is that if you use an operator combination that we haven't explicitly made a new version of then C++ is going to always cast the float to FractionalLayoutUnit (int without SUBPIXEL_LAYOUT set), and then perform the operation in FractionalLayoutUnit. Therefore it is good practise to always use toFloat() whenever we intend the operation to be performed in float. This is already been done in all other implementations except these two. So it brings the good practice and coding style to these two remaining cases.
Comment 8 Allan Sandfeld Jensen 2012-05-21 10:04:36 PDT
Created attachment 143053 [details]
Patch

Remove changes to a*b/c expressions. Darin is right, extra parenthesises are redundant.

Removed the explicit .toFloat() casts as well, and instead implemented a full set of operator X= operators. I have verified that this ensures the operations are performed in the correct types (at least for the combinations of FractionalLayoutUnit, int and float).
Comment 9 David Schlosnagle 2012-06-04 22:18:46 PDT
This patch will likely clash with committed changes in bug 88259.
Comment 10 Allan Sandfeld Jensen 2012-06-05 02:49:45 PDT
(In reply to comment #9)
> This patch will likely clash with committed changes in bug 88259.

Yep, they had the same problem this is supposed to prevent.
Comment 11 Levi Weintraub 2012-06-05 09:54:15 PDT
(In reply to comment #10)
> (In reply to comment #9)
> > This patch will likely clash with committed changes in bug 88259.
> 
> Yep, they had the same problem this is supposed to prevent.

I'd love to see this fixed, but I haven't had the chance to actually test for its impact on our tests, and the previous patch was uploaded before sub-pixel layout was enabled in Chromium. If you'd please update your patch and re-upload, the cr-linux bot will validate that there aren't any unforeseen results of this change.
Comment 12 Allan Sandfeld Jensen 2012-07-11 06:13:09 PDT
(In reply to comment #11)
> (In reply to comment #10)
> > (In reply to comment #9)
> > > This patch will likely clash with committed changes in bug 88259.
> > 
> > Yep, they had the same problem this is supposed to prevent.
> 
> I'd love to see this fixed, but I haven't had the chance to actually test for its impact on our tests, and the previous patch was uploaded before sub-pixel layout was enabled in Chromium. If you'd please update your patch and re-upload, the cr-linux bot will validate that there aren't any unforeseen results of this change.

Most of the issues have now been solved in different patches. I will rebase the rest though and reupload it.
Comment 13 Allan Sandfeld Jensen 2012-07-11 06:23:32 PDT
Created attachment 151695 [details]
Patch
Comment 14 Allan Sandfeld Jensen 2012-07-27 02:42:49 PDT
I think I finally figued out why some of the these operations are performed in the wrong type when they shouldn't.

Take the case:
FractionalLayoutUnit a, b;
a = a + b / 2;

This will cause a compile error because the statement is ambigious, cause it CAN NOT be performed in FractionalLayoutUnit, and instead has to be performed in int or floats.

The problem is that all the operators take 'const FractionalLayoutUnit&' as argument, and this prevents them from operating on the result of a previous operator, because return values are R-values, and you can only take references on L-values.

I don't think the solution is to use C++11 R-value references though. The FractionalLayoutUnit is only one int, so call by reference is a completely unnecessary optimization, that will only cause subtle bugs like this.

Does that make sense?
Comment 15 Allan Sandfeld Jensen 2012-07-27 03:06:30 PDT
(In reply to comment #14)
> Does that make sense?

I guess not. Sorry for the confusion.
Comment 16 Levi Weintraub 2012-07-31 13:28:37 PDT
Comment on attachment 151695 [details]
Patch

This does make sense, thanks for explaining it and taking the time to help us get it right!
Comment 17 Levi Weintraub 2012-07-31 13:29:10 PDT
(In reply to comment #14)
> I don't think the solution is to use C++11 R-value references though. The FractionalLayoutUnit is only one int, so call by reference is a completely unnecessary optimization, that will only cause subtle bugs like this.

Mind filing a bug to cover this?
Comment 18 WebKit Review Bot 2012-07-31 14:32:20 PDT
Comment on attachment 151695 [details]
Patch

Clearing flags on attachment: 151695

Committed r124253: <http://trac.webkit.org/changeset/124253>
Comment 19 WebKit Review Bot 2012-07-31 14:32:24 PDT
All reviewed patches have been landed.  Closing bug.
Comment 20 Allan Sandfeld Jensen 2012-07-31 15:41:17 PDT
(In reply to comment #17)
> (In reply to comment #14)
> > I don't think the solution is to use C++11 R-value references though. The FractionalLayoutUnit is only one int, so call by reference is a completely unnecessary optimization, that will only cause subtle bugs like this.
> 
> Mind filing a bug to cover this?

No, because I was wrong about the subtle bugs part. As long as it a const reference it can take references of R-values anyway. The reference is still unnecessary, but it is also unnecessary to change it.