aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/dc
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2017-12-07 02:08:55 +0000
committerAlan Somers <asomers@FreeBSD.org>2017-12-07 02:08:55 +0000
commit0d119bab3801d3bdac9c5fc0d8e64536197b67bc (patch)
tree91449b5bc84b91867178b26961eceac897e96c16 /usr.bin/dc
parentf3aff7c91bd451d300ffd37ddce5a1fdbe802123 (diff)
downloadsrc-0d119bab3801d3bdac9c5fc0d8e64536197b67bc.tar.gz
src-0d119bab3801d3bdac9c5fc0d8e64536197b67bc.zip
Notes
Diffstat (limited to 'usr.bin/dc')
-rw-r--r--usr.bin/dc/bcode.c39
-rw-r--r--usr.bin/dc/dc.16
-rw-r--r--usr.bin/dc/tests/Makefile1
-rwxr-xr-xusr.bin/dc/tests/bcode.sh144
-rwxr-xr-xusr.bin/dc/tests/inout.sh4
5 files changed, 179 insertions, 15 deletions
diff --git a/usr.bin/dc/bcode.c b/usr.bin/dc/bcode.c
index 0c77777b3976..0e8b73d967f8 100644
--- a/usr.bin/dc/bcode.c
+++ b/usr.bin/dc/bcode.c
@@ -1096,13 +1096,13 @@ bmod(void)
r = new_number();
scale = max(a->scale, b->scale);
- r->scale = max(b->scale, a->scale + bmachine.scale);
+ r->scale = scale;
if (BN_is_zero(a->number))
warnx("remainder by zero");
else {
normalize(a, scale);
- normalize(b, scale + bmachine.scale);
+ normalize(b, scale);
ctx = BN_CTX_new();
bn_checkp(ctx);
@@ -1117,7 +1117,7 @@ bmod(void)
static void
bdivmod(void)
{
- struct number *a, *b, *rdiv, *rmod;
+ struct number *a, *b, *frac, *quotient, *rdiv, *remainder;
BN_CTX *ctx;
u_int scale;
@@ -1131,25 +1131,44 @@ bdivmod(void)
}
rdiv = new_number();
- rmod = new_number();
- rdiv->scale = bmachine.scale;
- rmod->scale = max(b->scale, a->scale + bmachine.scale);
+ quotient = new_number();
+ remainder = new_number();
+ scale = max(a->scale, b->scale);
+ rdiv->scale = 0;
+ remainder->scale = scale;
+ quotient->scale = bmachine.scale;
scale = max(a->scale, b->scale);
if (BN_is_zero(a->number))
warnx("divide by zero");
else {
normalize(a, scale);
- normalize(b, scale + bmachine.scale);
+ normalize(b, scale);
ctx = BN_CTX_new();
bn_checkp(ctx);
- bn_check(BN_div(rdiv->number, rmod->number,
+ /*
+ * Unlike other languages' divmod operations, dc is specified
+ * to return the remainder and the full quotient, rather than
+ * the remainder and the floored quotient. bn(3) has no
+ * function to calculate both. So we'll use BN_div to get the
+ * remainder and floored quotient, then calculate the full
+ * quotient from those.
+ *
+ * quotient = rdiv + remainder / divisor
+ */
+ bn_check(BN_div(rdiv->number, remainder->number,
b->number, a->number, ctx));
+ frac = div_number(remainder, a, bmachine.scale);
+ normalize(rdiv, bmachine.scale);
+ normalize(remainder, scale);
+ bn_check(BN_add(quotient->number, rdiv->number, frac->number));
+ free_number(frac);
BN_CTX_free(ctx);
}
- push_number(rdiv);
- push_number(rmod);
+ push_number(quotient);
+ push_number(remainder);
+ free_number(rdiv);
free_number(a);
free_number(b);
}
diff --git a/usr.bin/dc/dc.1 b/usr.bin/dc/dc.1
index 43ba6cf01cd4..f712794bca6a 100644
--- a/usr.bin/dc/dc.1
+++ b/usr.bin/dc/dc.1
@@ -35,7 +35,7 @@
.\"
.\" @(#)dc.1 8.1 (Berkeley) 6/6/93
.\"
-.Dd February 27, 2017
+.Dd December 5, 2017
.Dt DC 1
.Os
.Sh NAME
@@ -139,8 +139,8 @@ The two entries are popped off the stack;
the result is pushed on the stack in their place.
Any fractional part of an exponent is ignored.
.Pp
-For addition and subtraction, the scale of the result is the maximum
-of scales of the operands.
+For addition, subtraction, and remainder, the scale of the result is the
+maximum of scales of the operands.
For division the scale of the result is defined
by the scale set by the
.Ic k
diff --git a/usr.bin/dc/tests/Makefile b/usr.bin/dc/tests/Makefile
index e97459e55e9e..2a33aad5a079 100644
--- a/usr.bin/dc/tests/Makefile
+++ b/usr.bin/dc/tests/Makefile
@@ -3,5 +3,6 @@
PACKAGE= tests
ATF_TESTS_SH= inout
+ATF_TESTS_SH+= bcode
.include <bsd.test.mk>
diff --git a/usr.bin/dc/tests/bcode.sh b/usr.bin/dc/tests/bcode.sh
new file mode 100755
index 000000000000..62c5aee438e0
--- /dev/null
+++ b/usr.bin/dc/tests/bcode.sh
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2017 Alan Somers
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+atf_test_case bmod
+bmod_head()
+{
+ atf_set "descr" "Tests the remainder % operator"
+}
+bmod_body()
+{
+ cat > input.dc << EOF
+0 3 % p # basic usage
+1 3 % p
+2 3 % p
+3 3 % p
+4 3 % p
+_1 3 % p # negative dividends work like a remainder, not a modulo
+1 _3 % p # negative divisors use the divisor's absolute value
+1k # fractional remainders
+5 3 % p
+6 5 % p
+5.4 3 % p
+_.1 3 % p
+1.1 _3 % p
+1 .3 % p
+EOF
+ dc input.dc > output.txt
+ cat > expect.txt << EOF
+0
+1
+2
+0
+1
+-1
+1
+2
+1
+2.4
+-.1
+1.1
+.1
+EOF
+ atf_check cmp expect.txt output.txt
+}
+
+atf_test_case bmod_by_zero
+bmod_by_zero_head()
+{
+ atf_set "descr" "remaindering by zero should print a warning"
+}
+bmod_by_zero_body()
+{
+ atf_check -e match:"remainder by zero" dc -e '1 0 %'
+}
+
+atf_test_case bdivmod
+bdivmod_head()
+{
+ atf_set "descr" "Tests the divide and modulo ~ operator"
+}
+bdivmod_body()
+{
+ cat > input.dc << EOF
+0 3 ~ n32Pp # basic usage
+1 3 ~ n32Pp
+2 3 ~ n32Pp
+3 3 ~ n32Pp
+4 3 ~ n32Pp
+_1 3 ~ n32Pp # negative dividends work like a remainder, not a modulo
+_4 3 ~ n32Pp # sign of quotient and divisor must agree
+1 _3 ~ n32Pp # negative divisors use the divisor's absolute value
+1k # fractional remainders
+5 3 ~ n32Pp
+6 5 ~ n32Pp
+5.4 3 ~ n32Pp
+_.1 3 ~ n32Pp
+1.1 _3 ~ n32Pp
+1 .3 ~ n32Pp
+4k
+.01 .003 ~ n32Pp # divmod quotient always has scale=0
+EOF
+ dc input.dc > output.txt
+ cat > expect.txt << EOF
+0 0
+1 0
+2 0
+0 1
+1 1
+-1 0
+-1 -1
+1 0
+2 1.6
+1 1.2
+2.4 1.8
+-.1 0.0
+1.1 -.3
+.1 3.3
+.001 3.3333
+EOF
+ atf_check cmp expect.txt output.txt
+}
+
+atf_test_case bdivmod_by_zero
+bdivmod_by_zero_head()
+{
+ atf_set "descr" "divmodding by zero should print a warning"
+}
+bdivmod_by_zero_body()
+{
+ atf_check -e match:"divide by zero" dc -e '1 0 ~'
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case bmod
+ atf_add_test_case bmod_by_zero
+ atf_add_test_case bdivmod
+ atf_add_test_case bdivmod_by_zero
+}
diff --git a/usr.bin/dc/tests/inout.sh b/usr.bin/dc/tests/inout.sh
index e06130aeddf0..3a81f8f708b1 100755
--- a/usr.bin/dc/tests/inout.sh
+++ b/usr.bin/dc/tests/inout.sh
@@ -52,8 +52,8 @@ base16_input_body()
0.1 p # Leading zeros are ignored
00.1 p # Leading zeros are ignored
EOF
-dc input.dc > output.txt
-cat > expect.txt << EOF
+ dc input.dc > output.txt
+ cat > expect.txt << EOF
0
16
1