@@ -18,6 +18,10 @@ import (
18
18
)
19
19
20
20
var _ planIter = (* arithOpIter )(nil )
21
+ var (
22
+ divByZeroErr = nosqlerr .NewIllegalState ("arithmetic operation failed: divide by zero" )
23
+ ratZero = new (big.Rat ).SetInt64 (0 )
24
+ )
21
25
22
26
// arithOpIter represents a plan iterator that implements the arithmetic operations.
23
27
//
@@ -159,13 +163,7 @@ func (iter *arithOpIter) next(rcb *runtimeControlBlock) (more bool, err error) {
159
163
opResult = int (1 )
160
164
}
161
165
162
- // Recover from panics such as "divide by zero".
163
- defer func () {
164
- if e := recover (); e != nil {
165
- rcb .trace (1 , "arithOpIter.next(): %v" , e )
166
- err = fmt .Errorf ("arithmetic operation failed: %v" , e )
167
- }
168
- }()
166
+ fpArithSpec := rcb .getRequest ().getFPArithSpec ()
169
167
170
168
for i , argIter := range iter .argIters {
171
169
more , err = argIter .next (rcb )
@@ -180,7 +178,7 @@ func (iter *arithOpIter) next(rcb *runtimeControlBlock) (more bool, err error) {
180
178
181
179
argVal := argIter .getResult (rcb )
182
180
if argVal == types .NullValueInstance {
183
- rcb .trace (1 , "argVal: %v, ================= got nullValue" , argVal )
181
+ rcb .trace (1 , "argVal: %v, got nullValue" , argVal )
184
182
rcb .setRegValue (iter .resultReg , types .NullValueInstance )
185
183
state .done ()
186
184
return true , nil
@@ -189,13 +187,25 @@ func (iter *arithOpIter) next(rcb *runtimeControlBlock) (more bool, err error) {
189
187
op := iter .ops [i ]
190
188
switch argVal := argVal .(type ) {
191
189
case int :
192
- opResult , err = calcInt (op , opResult , argVal )
190
+ if (op == '/' || op == 'd' ) && argVal == 0 {
191
+ return false , divByZeroErr
192
+ }
193
+ opResult , err = calcInt (op , opResult , argVal , fpArithSpec )
193
194
case int64 :
194
- opResult , err = calcInt64 (op , opResult , argVal )
195
+ if (op == '/' || op == 'd' ) && argVal == 0 {
196
+ return false , divByZeroErr
197
+ }
198
+ opResult , err = calcInt64 (op , opResult , argVal , fpArithSpec )
195
199
case float64 :
196
- opResult , err = calcFloat64 (op , opResult , argVal )
200
+ if (op == '/' || op == 'd' ) && argVal == 0 {
201
+ return false , divByZeroErr
202
+ }
203
+ opResult , err = calcFloat64 (op , opResult , argVal , fpArithSpec )
197
204
case * big.Rat :
198
- opResult , err = calcBigRat (op , opResult , argVal , rcb .getRequest ().getFPArithSpec ())
205
+ if (op == '/' || op == 'd' ) && argVal .Cmp (ratZero ) == 0 {
206
+ return false , divByZeroErr
207
+ }
208
+ opResult , err = calcBigRat (op , opResult , argVal , fpArithSpec )
199
209
default :
200
210
return false , nosqlerr .NewIllegalState ("operand in " +
201
211
"arithmetic operation has illegal type. " +
@@ -207,14 +217,14 @@ func (iter *arithOpIter) next(rcb *runtimeControlBlock) (more bool, err error) {
207
217
}
208
218
}
209
219
210
- rcb .trace (1 , "============ arithOp result: %v" , opResult )
220
+ rcb .trace (1 , "arithOp result: %v" , opResult )
211
221
rcb .setRegValue (iter .resultReg , opResult )
212
222
state .done ()
213
223
return true , nil
214
224
}
215
225
216
226
// caclInt calculates the result of applying the specified operation to the operand of int type.
217
- func calcInt (op byte , currRes interface {}, v int ) (res interface {}, err error ) {
227
+ func calcInt (op byte , currRes interface {}, v int , fpSpec * FPArithSpec ) (res interface {}, err error ) {
218
228
switch r := currRes .(type ) {
219
229
case int :
220
230
switch op {
@@ -258,15 +268,17 @@ func calcInt(op byte, currRes interface{}, v int) (res interface{}, err error) {
258
268
259
269
switch op {
260
270
case '+' :
261
- res = r .Add (r , newVal )
271
+ r = r .Add (r , newVal )
262
272
case '-' :
263
- res = r .Sub (r , newVal )
273
+ r = r .Sub (r , newVal )
264
274
case '*' :
265
- res = r .Mul (r , newVal )
275
+ r = r .Mul (r , newVal )
266
276
case '/' , 'd' :
267
- res = r .Quo (r , newVal )
277
+ r = r .Quo (r , newVal )
268
278
}
269
279
280
+ res = setPrecAndRounding (r , fpSpec )
281
+
270
282
default :
271
283
return currRes , fmt .Errorf ("unsupported result type for arithmetic operation: %T" , currRes )
272
284
}
@@ -279,7 +291,7 @@ func calcInt(op byte, currRes interface{}, v int) (res interface{}, err error) {
279
291
}
280
292
281
293
// calcInt64 calculates the result of applying the specified operation to the operand of int64 type.
282
- func calcInt64 (op byte , currRes interface {}, v int64 ) (res interface {}, err error ) {
294
+ func calcInt64 (op byte , currRes interface {}, v int64 , fpSpec * FPArithSpec ) (res interface {}, err error ) {
283
295
switch r := currRes .(type ) {
284
296
case int :
285
297
switch op {
@@ -323,15 +335,17 @@ func calcInt64(op byte, currRes interface{}, v int64) (res interface{}, err erro
323
335
324
336
switch op {
325
337
case '+' :
326
- res = r .Add (r , newVal )
338
+ r = r .Add (r , newVal )
327
339
case '-' :
328
- res = r .Sub (r , newVal )
340
+ r = r .Sub (r , newVal )
329
341
case '*' :
330
- res = r .Mul (r , newVal )
342
+ r = r .Mul (r , newVal )
331
343
case '/' , 'd' :
332
- res = r .Quo (r , newVal )
344
+ r = r .Quo (r , newVal )
333
345
}
334
346
347
+ res = setPrecAndRounding (r , fpSpec )
348
+
335
349
default :
336
350
return currRes , fmt .Errorf ("unsupported result type for arithmetic operation: %T" , currRes )
337
351
}
@@ -344,7 +358,7 @@ func calcInt64(op byte, currRes interface{}, v int64) (res interface{}, err erro
344
358
}
345
359
346
360
// calcFloat64 calculates the result of applying the specified operation to the operand of float64 type.
347
- func calcFloat64 (op byte , currRes interface {}, v float64 ) (res interface {}, err error ) {
361
+ func calcFloat64 (op byte , currRes interface {}, v float64 , fpSpec * FPArithSpec ) (res interface {}, err error ) {
348
362
switch r := currRes .(type ) {
349
363
case int :
350
364
switch op {
@@ -388,15 +402,17 @@ func calcFloat64(op byte, currRes interface{}, v float64) (res interface{}, err
388
402
389
403
switch op {
390
404
case '+' :
391
- res = r .Add (r , newVal )
405
+ r = r .Add (r , newVal )
392
406
case '-' :
393
- res = r .Sub (r , newVal )
407
+ r = r .Sub (r , newVal )
394
408
case '*' :
395
- res = r .Mul (r , newVal )
409
+ r = r .Mul (r , newVal )
396
410
case '/' , 'd' :
397
- res = r .Quo (r , newVal )
411
+ r = r .Quo (r , newVal )
398
412
}
399
413
414
+ res = setPrecAndRounding (r , fpSpec )
415
+
400
416
default :
401
417
return currRes , fmt .Errorf ("unsupported result type for arithmetic operation: %T" , currRes )
402
418
}
@@ -430,23 +446,46 @@ func calcBigRat(op byte, currRes interface{}, v *big.Rat, fpSpec *FPArithSpec) (
430
446
431
447
switch op {
432
448
case '+' :
433
- rat .Add (rat , v )
449
+ rat = rat .Add (rat , v )
434
450
case '-' :
435
- rat .Sub (rat , v )
451
+ rat = rat .Sub (rat , v )
436
452
case '*' :
437
- rat .Mul (rat , v )
453
+ rat = rat .Mul (rat , v )
438
454
case '/' , 'd' :
439
- rat .Quo (rat , v )
455
+ rat = rat .Quo (rat , v )
440
456
default :
441
457
return 0 , fmt .Errorf ("unsupported operation: %q" , string (op ))
442
458
}
443
459
444
- // TODO: apply the specified FPArithSpec to the result
445
-
446
- res = rat
460
+ res = setPrecAndRounding (rat , fpSpec )
447
461
return res , nil
448
462
}
449
463
464
+ // binaryPrecision returns the closest binary precision for the specified decimal precision.
465
+ func binaryPrecision (decimalPrec uint ) (prec uint ) {
466
+ switch decimalPrec {
467
+ case 7 :
468
+ return 24
469
+ case 16 :
470
+ return 53
471
+ case 34 :
472
+ return 113
473
+ default :
474
+ return 0
475
+ }
476
+ }
477
+
478
+ func setPrecAndRounding (v * big.Rat , fpSpec * FPArithSpec ) * big.Rat {
479
+ if fpSpec == nil {
480
+ return v
481
+ }
482
+
483
+ prec := binaryPrecision (fpSpec .Precision )
484
+ bf := new (big.Float ).SetRat (v ).SetPrec (prec ).SetMode (fpSpec .RoundingMode )
485
+ res , _ := bf .Rat (v )
486
+ return res
487
+ }
488
+
450
489
func (iter * arithOpIter ) getPlan () string {
451
490
return iter .planIterDelegate .getExecPlan (iter )
452
491
}
0 commit comments