From 2c4278d83b2cf2de31d8d5a4d9f259e5cf0134f6 Mon Sep 17 00:00:00 2001 From: Mad Dinh Date: Mon, 24 Aug 2026 09:42:24 -0700 Subject: [PATCH] Fix Double prop defaults losing precision in generated Android delegates (#58070) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: `GeneratePropsJavaDelegate` emits the default value for an optional `DoubleTypeAnnotation` prop with an `f` (float) suffix, but the setter it calls takes a `double`: ```js // GeneratePropsJavaDelegate.js case 'DoubleTypeAnnotation': if (prop.optional) { return `value == null ? ${typeAnnotation.default}f : ((Double) value).doubleValue()`; } ``` ```js // GeneratePropsJavaInterface.js — the setter this is passed to case 'DoubleTypeAnnotation': return 'double value'; ``` The `f` literal is parsed as a `float`, then widened back to `double` at the call site, so the default silently arrives rounded to float precision. It compiles without a warning, which is why it has gone unnoticed. This is visible in the repo's own snapshots today. Same fixture, same prop, two platforms: | Generator | Output for `blurRadius3?: WithDefault` | | --- | --- | | `GeneratePropsH` (C++) | `double blurRadius3{2.1};` | | `GeneratePropsJavaDelegate` | `value == null ? 2.1f : ((Double) value).doubleValue()` | So a component using the C++ renderer gets `2.1` and the same component on the Android delegate path gets `2.0999999046325684`. Compiling the generated shape confirms it: ```java static void setBlurRadius3(double value) { System.out.println(value); } setBlurRadius3(value == null ? 2.1f : ...); // 2.0999999046325684 setBlurRadius3(value == null ? 123456789f : ...); // 1.23456792E8 ``` The integer case is the clearest damage: any `Double` default above 2^24 is not representable as a `float`, so `123456789` arrives as `123456792`. Fractional defaults are wrong from the first value that is not a dyadic rational — `0.1`, `2.1`, and the fixture's own `0.001` all shift. `FloatTypeAnnotation` on the line below is correct — it targets a `float` setter, so `f` is right there. Only the `Double` branch has the wrong suffix, which is consistent with it having been copied from the `Float` branch. Changed to a `d` suffix, mirroring the existing `f` for float rather than dropping the suffix entirely, so the literal's type is stated rather than left to numeric promotion. `DoubleTypeAnnotation` is never boxed by codegen (it is always a primitive `double`, defaulting to `Double.NaN` when non-optional), so there is no nullable-double path to consider. ## Changelog: [ANDROID] [FIXED] - Fix `Double` prop defaults being rounded to float precision in generated `ViewManager` delegates Pull Request resolved: https://github.com/react/react-native/pull/58070 Test Plan: The generator is snapshot-tested, so the snapshot is the test. Updating it changes only the `Double` component and leaves the `Float` component untouched: ```diff public class DoublePropNativeComponentManagerDelegate