Bug 86065 - FractionalLayoutUnit minor math bugs
: FractionalLayoutUnit minor math bugs
Status: RESOLVED FIXED
: WebKit
Layout and Rendering
: 528+ (Nightly build)
: Unspecified Unspecified
: P2 Normal
Assigned To:
:
:
:
:
  Show dependency treegraph
 
Reported: 2012-05-10 01:04 PST by
Modified: 2012-07-31 15:41 PST (History)


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


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2012-05-10 01:04:05 PST
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 From 2012-05-10 01:13:55 PST -------
Created an attachment (id=141105) [details]
Patch
------- Comment #2 From 2012-05-10 10:26:44 PST -------
(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 :)

> 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 From 2012-05-10 11:28:06 PST -------
(In reply to comment #2)
> (From update of attachment 141105 [details] [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 From 2012-05-11 00:49:57 PST -------
Created an attachment (id=141345) [details]
Patch
------- Comment #5 From 2012-05-12 08:48:04 PST -------
(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: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 From 2012-05-12 08:50:31 PST -------
(From update of attachment 141345 [details])
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 From 2012-05-12 09:54:21 PST -------
(In reply to comment #5)
> (From update of attachment 141345 [details] [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 From 2012-05-21 10:04:36 PST -------
Created an attachment (id=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 From 2012-06-04 22:18:46 PST -------
This patch will likely clash with committed changes in bug 88259.
------- Comment #10 From 2012-06-05 02:49:45 PST -------
(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 From 2012-06-05 09:54:15 PST -------
(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 From 2012-07-11 06:13:09 PST -------
(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 From 2012-07-11 06:23:32 PST -------
Created an attachment (id=151695) [details]
Patch
------- Comment #14 From 2012-07-27 02:42:49 PST -------
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 From 2012-07-27 03:06:30 PST -------
(In reply to comment #14)
> Does that make sense?

I guess not. Sorry for the confusion.
------- Comment #16 From 2012-07-31 13:28:37 PST -------
(From update of attachment 151695 [details])
This does make sense, thanks for explaining it and taking the time to help us get it right!
------- Comment #17 From 2012-07-31 13:29:10 PST -------
(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 From 2012-07-31 14:32:20 PST -------
(From update of attachment 151695 [details])
Clearing flags on attachment: 151695

Committed r124253: <http://trac.webkit.org/changeset/124253>
------- Comment #19 From 2012-07-31 14:32:24 PST -------
All reviewed patches have been landed.  Closing bug.
------- Comment #20 From 2012-07-31 15:41:17 PST -------
(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.