@Test
  public void testMultiply() {
    Decimal128 result = new Decimal128();
    Decimal128.multiply(one, two, result, (short) 2);
    assertEquals(0, two.compareTo(result));

    Decimal128.multiply(two, two, result, (short) 2);
    assertEquals(0, new Decimal128(4L, (short) 0).compareTo(result));

    long l1 = 123456789012345L;
    long l2 = 987654321097L;
    Decimal128 left = new Decimal128(l1, (short) 0);
    Decimal128 right = new Decimal128(l2, (short) 0);
    UnsignedInt128 unscaled = new UnsignedInt128(l1)
        .multiplyConstructive(new UnsignedInt128(l2));
    Decimal128 ans = new Decimal128(unscaled, (short) 0, false);
    Decimal128.multiply(left, right, result, (short) 0);
    assertEquals(0, ans.compareTo(result));
    Decimal128.multiply(right, left, result, (short) 0);
    assertEquals(0, ans.compareTo(result));

    Decimal128.multiply(new Decimal128(1.123d, (short) 10), new Decimal128(
        4.321d, (short) 10), result, (short) 10);
    assertEquals(1.123d * 4.321d, result.doubleValue(), 0.00001d);

    // because only 10 fractional digits, it's not this much accurate
    assertNotEquals(1.123d * 4.321d, result.doubleValue(), 0.00000000000000001d);

    Decimal128.multiply(new Decimal128(1.123d, (short) 2), new Decimal128(
        4.321d, (short) 2), result, (short) 2);

    // this time even more inaccurate
    assertEquals(1.123d * 4.321d, result.doubleValue(), 1.0d);
    assertNotEquals(1.123d * 4.321d, result.doubleValue(), 0.000001d);

    Decimal128 val = new Decimal128("1.123", (short) 3);
    val.multiplyDestructive(new Decimal128("4.321", (short) 3), (short) 6);
    assertEquals("4.852483", val.toFormalString());

    Decimal128 val1 = new Decimal128("1.0001", (short) 4);
    val1.multiplyDestructive(new Decimal128("1.0001", (short) 4), (short) 8);
    assertEquals("1.00020001", val1.toFormalString());

  }
This code was based on code from Microsoft's PolyBase.
common/src/test/org/apache/hadoop/hive/common/type/TestDecimal128.java