On Wed, 19 Aug 2026 05:06:30 GMT, Michael Strauß <[email protected]> wrote:

>> There's two things here:
>> 
>> 1. Using `ScaledMath.ceil` where it subtracts the `ulp` works incorrectly 
>> when inputs are so large that they don't have a significant fractional part 
>> anymore (all digits are allocated to the integer part) -- this causes 
>> `Double.MAX_VALUE` to get mangled which shows up in unit tests that can't 
>> just use `== Double.MAX_VALUE` -- the rounding functions have no such 
>> problems
>> 2. The current `ceil` function forces absolute correct use of snapping, so 
>> that even with pre-snapped values `left + size + right` must be snapped 
>> again. With a more lenient `ceil` this final snapping becomes unnecessary as 
>> the total error introduced with pre-snapped values will never become 
>> significant enough that we can't `ceil` to the **intended pixel** if we were 
>> using a constant instead of the `ulp`.
>> 
>> ## Example for situation 1:
>> ### Using ULP
>> Initial value | ULP | ceil(initial − ULP) | Difference
>> ---|---|---|---
>> 1.797693134862**3157**E308 | 1.9958403095347198E292 | 
>> 1.797693134862**3155**E308 | 2E292
>> 2.0E20 | 3.2768E4 | 1.9999999999999997E20 | 30000
>> 2.0E10 | 3.814697265625E-6 | 2.0E10 | 0
>> 2.0E5 | 2.9103830456733704E-11 | 2.0E5 | 0
>> 2.0E0 | 4.440892098500626E-16 | 2.0E0 | 0
>> 
>> ### Using tiny fractional constant
>> Initial value | Constant | ceil(initial − Constant) | Difference
>> ---|---|---|---
>> 1.7976931348623157E308 | 1.0E-6 | 1.7976931348623157E308 | 0
>> 2.0E20 | 1.0E-6 | 2.0E20 | 0
>> 2.0E10 | 1.0E-6 | 2.0E10 | 0
>> 2.0E5 | 1.0E-6 | 2.0E5 | 0
>> 2.0E0 | 1.0E-6 | 2.0E0 | 0
>
> We've still lost the information to decide whether 10.0000001 is a tiny, but 
> intended offset that should round to 11, or whether it is noise and the 
> result should round to 10.
> 
> But let's assume we make the ceil function a bit fuzzy, so that it tolerates 
> arithmetic on snapped values where the final result is not snapped again. 
> That doesn't make the snapping API a good idea, or even just remotely useful 
> for custom controls, because even with the snap-the-result rule gone, there 
> are still a lot of other ways in which developers will definitely get 
> snapping wrong.
> 
> I think the only way how third-party developers can create custom layouts 
> that actually work is to offer an API to compose them from well-tested 
> primitives, like your virtual layout container proposal.

> It is true that your idea improves perturbation stability of sloppily-snapped 
> calculations, but I think that it doesn't improve the result if the snapping 
> was done correctly to begin with (that is, discard any arithmetic excess if 
> you know that your calculation was done on pixel-aligned values)

Yeah, but that does set a very high bar. Even a simple addition like `return 
left + size + right` is wrong even when all values were snapped here. What I'm 
suggesting is that with a slightly less "edgy" ceil function, you only need one 
rule: 

- snap all input values before use

If we didn't have `ceil` anywhere, but all functions used rounding, then that 
would have been sufficient. And that seems to have been the thought process 
behind the implementation of almost all layout containers (they snap their 
inputs, then calculate without further snapping).

However `snapSize`, which intent is to **snap a content-size to a size so all 
of it is visible**, was implemented in a strict mathematical sense that is 
sensitive to tiny floating point errors; in reality it was intended to ensure 
all content pixels were visible; do we really need to ensure that an overflow 
of 0.00000000000002 is "visible" ? That extra pixel allocated there will be 
blank (even with anti-aliasing).

So I agree that `ceil` as implemented now is correct in the mathematical sense, 
but that's not what we're trying to achieve. We're trying to achieve correct 
snapping for content sizes, not ceiling.

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/2260#discussion_r3810205104

Reply via email to