Skip to content

Commit 7f1ef49

Browse files
griesemerdmitshur
authored andcommitted
[release-branch.go1.13] math/big: normalize unitialized denominators ASAP
A Rat is represented via a quotient a/b where a and b are Int values. To make it possible to use an uninitialized Rat value (with a and b uninitialized and thus == 0), the implementation treats a 0 denominator as 1. For each operation we check if the denominator is 0, and then treat it as 1 (if necessary). Operations that create a new Rat result, normalize that value such that a result denominator 1 is represened as 0 again. This CL changes this behavior slightly: 0 denominators are still interpreted as 1, but whenever we (safely) can, we set an uninitialized 0 denominator to 1. This simplifies the code overall. Also: Improved some doc strings. Preparation for addressing issue #33792. For #36689. For #33792. Change-Id: I3040587c8d0dad2e840022f96ca027d8470878a0 Reviewed-on: https://go-review.googlesource.com/c/go/+/202997 Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/233322 Run-TryBot: Dmitri Shuralyov <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]>
1 parent 444ad95 commit 7f1ef49

File tree

1 file changed

+28
-27
lines changed

1 file changed

+28
-27
lines changed

src/math/big/rat.go

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import (
2222
// of Rats are not supported and may lead to errors.
2323
type Rat struct {
2424
// To make zero values for Rat work w/o initialization,
25-
// a zero value of b (len(b) == 0) acts like b == 1.
25+
// a zero value of b (len(b) == 0) acts like b == 1. At
26+
// the earliest opportunity (when an assignment to the Rat
27+
// is made), such uninitialized denominators are set to 1.
2628
// a.neg determines the sign of the Rat, b.neg is ignored.
2729
a, b Int
2830
}
@@ -297,6 +299,7 @@ func (x *Rat) Float64() (f float64, exact bool) {
297299
}
298300

299301
// SetFrac sets z to a/b and returns z.
302+
// If b == 0, SetFrac panics.
300303
func (z *Rat) SetFrac(a, b *Int) *Rat {
301304
z.a.neg = a.neg != b.neg
302305
babs := b.abs
@@ -312,11 +315,12 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
312315
}
313316

314317
// SetFrac64 sets z to a/b and returns z.
318+
// If b == 0, SetFrac64 panics.
315319
func (z *Rat) SetFrac64(a, b int64) *Rat {
316-
z.a.SetInt64(a)
317320
if b == 0 {
318321
panic("division by zero")
319322
}
323+
z.a.SetInt64(a)
320324
if b < 0 {
321325
b = -b
322326
z.a.neg = !z.a.neg
@@ -328,21 +332,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
328332
// SetInt sets z to x (by making a copy of x) and returns z.
329333
func (z *Rat) SetInt(x *Int) *Rat {
330334
z.a.Set(x)
331-
z.b.abs = z.b.abs[:0]
335+
z.b.abs = z.b.abs.setWord(1)
332336
return z
333337
}
334338

335339
// SetInt64 sets z to x and returns z.
336340
func (z *Rat) SetInt64(x int64) *Rat {
337341
z.a.SetInt64(x)
338-
z.b.abs = z.b.abs[:0]
342+
z.b.abs = z.b.abs.setWord(1)
339343
return z
340344
}
341345

342346
// SetUint64 sets z to x and returns z.
343347
func (z *Rat) SetUint64(x uint64) *Rat {
344348
z.a.SetUint64(x)
345-
z.b.abs = z.b.abs[:0]
349+
z.b.abs = z.b.abs.setWord(1)
346350
return z
347351
}
348352

@@ -352,6 +356,9 @@ func (z *Rat) Set(x *Rat) *Rat {
352356
z.a.Set(&x.a)
353357
z.b.Set(&x.b)
354358
}
359+
if len(z.b.abs) == 0 {
360+
z.b.abs = z.b.abs.setWord(1)
361+
}
355362
return z
356363
}
357364

@@ -370,20 +377,13 @@ func (z *Rat) Neg(x *Rat) *Rat {
370377
}
371378

372379
// Inv sets z to 1/x and returns z.
380+
// If x == 0, Inv panics.
373381
func (z *Rat) Inv(x *Rat) *Rat {
374382
if len(x.a.abs) == 0 {
375383
panic("division by zero")
376384
}
377385
z.Set(x)
378-
a := z.b.abs
379-
if len(a) == 0 {
380-
a = a.set(natOne) // materialize numerator (a is part of z!)
381-
}
382-
b := z.a.abs
383-
if b.cmp(natOne) == 0 {
384-
b = b[:0] // normalize denominator
385-
}
386-
z.a.abs, z.b.abs = a, b // sign doesn't change
386+
z.a.abs, z.b.abs = z.b.abs, z.a.abs
387387
return z
388388
}
389389

@@ -424,25 +424,20 @@ func (x *Rat) Denom() *Int {
424424
func (z *Rat) norm() *Rat {
425425
switch {
426426
case len(z.a.abs) == 0:
427-
// z == 0 - normalize sign and denominator
427+
// z == 0; normalize sign and denominator
428428
z.a.neg = false
429-
z.b.abs = z.b.abs[:0]
429+
fallthrough
430430
case len(z.b.abs) == 0:
431-
// z is normalized int - nothing to do
432-
case z.b.abs.cmp(natOne) == 0:
433-
// z is int - normalize denominator
434-
z.b.abs = z.b.abs[:0]
431+
// z is integer; normalize denominator
432+
z.b.abs = z.b.abs.setWord(1)
435433
default:
434+
// z is fraction; normalize numerator and denominator
436435
neg := z.a.neg
437436
z.a.neg = false
438437
z.b.neg = false
439438
if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 {
440439
z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
441440
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
442-
if z.b.abs.cmp(natOne) == 0 {
443-
// z is int - normalize denominator
444-
z.b.abs = z.b.abs[:0]
445-
}
446441
}
447442
z.a.neg = neg
448443
}
@@ -454,6 +449,8 @@ func (z *Rat) norm() *Rat {
454449
// returns z.
455450
func mulDenom(z, x, y nat) nat {
456451
switch {
452+
case len(x) == 0 && len(y) == 0:
453+
return z.setWord(1)
457454
case len(x) == 0:
458455
return z.set(y)
459456
case len(y) == 0:
@@ -509,10 +506,14 @@ func (z *Rat) Sub(x, y *Rat) *Rat {
509506
// Mul sets z to the product x*y and returns z.
510507
func (z *Rat) Mul(x, y *Rat) *Rat {
511508
if x == y {
512-
// a squared Rat is positive and can't be reduced
509+
// a squared Rat is positive and can't be reduced (no need to call norm())
513510
z.a.neg = false
514511
z.a.abs = z.a.abs.sqr(x.a.abs)
515-
z.b.abs = z.b.abs.sqr(x.b.abs)
512+
if len(x.b.abs) == 0 {
513+
z.b.abs = z.b.abs.setWord(1)
514+
} else {
515+
z.b.abs = z.b.abs.sqr(x.b.abs)
516+
}
516517
return z
517518
}
518519
z.a.Mul(&x.a, &y.a)
@@ -521,7 +522,7 @@ func (z *Rat) Mul(x, y *Rat) *Rat {
521522
}
522523

523524
// Quo sets z to the quotient x/y and returns z.
524-
// If y == 0, a division-by-zero run-time panic occurs.
525+
// If y == 0, Quo panics.
525526
func (z *Rat) Quo(x, y *Rat) *Rat {
526527
if len(y.a.abs) == 0 {
527528
panic("division by zero")

0 commit comments

Comments
 (0)