From 5605b607d1f5abd51d357385053d4c990c0a54ec Mon Sep 17 00:00:00 2001 From: Chessing234 Date: Mon, 1 Jun 2026 13:37:28 +0530 Subject: [PATCH] fix: reduce sin/cos angle modulo 360 to avoid precision loss Math.sin and Math.cos lose precision for very large angle inputs because the radian argument (PI * n / 180) accumulates floating-point error. Since the trig functions are periodic with period 360 degrees, reduce the angle modulo 360 before converting to radians, matching the handling already done in MathUtil.tan. Fixes #2199 --- src/blocks/scratch3_operators.js | 4 ++-- test/unit/blocks_operators.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js index 4fd6a7bf42b..c4f67d121fd 100644 --- a/src/blocks/scratch3_operators.js +++ b/src/blocks/scratch3_operators.js @@ -136,8 +136,8 @@ class Scratch3OperatorsBlocks { case 'floor': return Math.floor(n); case 'ceiling': return Math.ceil(n); case 'sqrt': return Math.sqrt(n); - case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10)); - case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10)); + case 'sin': return parseFloat(Math.sin((Math.PI * (n % 360)) / 180).toFixed(10)); + case 'cos': return parseFloat(Math.cos((Math.PI * (n % 360)) / 180).toFixed(10)); case 'tan': return MathUtil.tan(n); case 'asin': return (Math.asin(n) * 180) / Math.PI; case 'acos': return (Math.acos(n) * 180) / Math.PI; diff --git a/test/unit/blocks_operators.js b/test/unit/blocks_operators.js index 5dbe082e941..4f3aec303ac 100644 --- a/test/unit/blocks_operators.js +++ b/test/unit/blocks_operators.js @@ -176,6 +176,9 @@ test('mathop', t => { t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 1}), 0.0174550649); t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 90}), Infinity); t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 180}), 0); + // Large angles should be reduced modulo 360 to avoid precision loss (#2199) + t.strictEqual(blocks.mathop({OPERATOR: 'sin', NUM: 36000000000090}), 1); + t.strictEqual(blocks.mathop({OPERATOR: 'cos', NUM: 36000000000090}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'asin', NUM: 1}), 90); t.strictEqual(blocks.mathop({OPERATOR: 'acos', NUM: 1}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'atan', NUM: 1}), 45);