File tree Expand file tree Collapse file tree 3 files changed +65
-2
lines changed
Expand file tree Collapse file tree 3 files changed +65
-2
lines changed Original file line number Diff line number Diff line change @@ -182,6 +182,11 @@ private function narrowArithmetic(
182182 return $ methodReflection ->getThrowType ();
183183 }
184184
185+ // For plus/minus/multipliedBy with int arg, rounding is impossible:
186+ // int arithmetic on a Money value always produces an exact result
187+ // that fits the same context (any scale, any step).
188+ $ intArgSuppressesRounding = $ methodName !== 'dividedBy ' && SafeType::isInteger ($ argType );
189+
185190 // Money: build residual throw types.
186191 $ residualTypes = [];
187192
@@ -195,8 +200,8 @@ private function narrowArithmetic(
195200 $ residualTypes [] = new ObjectType (NumberFormatException::class);
196201 }
197202
198- // RoundingNecessaryException when rounding mode is not safe.
199- if (! $ roundingModeIsSafe ) {
203+ // RoundingNecessaryException when rounding mode is not safe and int arg doesn't suppress it .
204+ if (! $ roundingModeIsSafe && ! $ intArgSuppressesRounding ) {
200205 $ residualTypes [] = new ObjectType (RoundingNecessaryException::class);
201206 }
202207
Original file line number Diff line number Diff line change @@ -80,6 +80,17 @@ public static function isSafeRoundingMode(Type $type): bool
8080 return $ unnecessaryType ->isSuperTypeOf ($ type )->no ();
8181 }
8282
83+ /**
84+ * Returns whether the given type is an integer.
85+ *
86+ * Integer arithmetic (plus, minus, multipliedBy) on Money always produces
87+ * an exact result that fits the same context, so rounding cannot occur.
88+ */
89+ public static function isInteger (Type $ type ): bool
90+ {
91+ return (new IntegerType ())->isSuperTypeOf ($ type )->yes ();
92+ }
93+
8394 /**
8495 * Returns whether the given type is guaranteed to be zero.
8596 *
Original file line number Diff line number Diff line change @@ -212,6 +212,53 @@ public function dividedByWithUnnecessaryRounding(Money $a): void
212212 }
213213 }
214214
215+ // --- Int arithmetic (no rounding mode needed) ---
216+
217+ public function multipliedByInt (Money $ a ): void
218+ {
219+ try {
220+ $ result = $ a ->multipliedBy (5 );
221+ } finally {
222+ assertVariableCertainty (TrinaryLogic::createYes (), $ result );
223+ }
224+ }
225+
226+ public function plusWithInt (Money $ a ): void
227+ {
228+ try {
229+ $ result = $ a ->plus (10 );
230+ } finally {
231+ assertVariableCertainty (TrinaryLogic::createYes (), $ result );
232+ }
233+ }
234+
235+ public function minusWithInt (Money $ a ): void
236+ {
237+ try {
238+ $ result = $ a ->minus (10 );
239+ } finally {
240+ assertVariableCertainty (TrinaryLogic::createYes (), $ result );
241+ }
242+ }
243+
244+ public function dividedByIntNoRoundingMode (Money $ a ): void
245+ {
246+ try {
247+ $ result = $ a ->dividedBy (3 );
248+ } finally {
249+ assertVariableCertainty (TrinaryLogic::createMaybe (), $ result );
250+ }
251+ }
252+
253+ public function multipliedByString (Money $ a , string $ s ): void
254+ {
255+ try {
256+ $ result = $ a ->multipliedBy ($ s );
257+ } finally {
258+ assertVariableCertainty (TrinaryLogic::createMaybe (), $ result );
259+ }
260+ }
261+
215262 // --- RationalMoney arithmetic ---
216263
217264 public function rationalPlusWithInt (RationalMoney $ a ): void
You can’t perform that action at this time.
0 commit comments