mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-12-04 03:49:02 +00:00
seq(1): Consistently include 'last' for non-integers
The source of error is a rounded increment being too large and thus the loop steps slightly past 'last'. Perform a final comparison using the formatted string values (truncated precision) to determine if we still need to print the 'last' value. PR: 217149 Submitted by: Fernando Apesteguía <fernando.apesteguia AT gmail.com>, Yuri Pankov <yuripv AT icloud.com> (earlier version) Reported by: Martijn Dekker <mcdutchie AT hotmail.com> Sponsored by: Dell EMC Isilon
This commit is contained in:
parent
3acf1760b7
commit
3049d4ccc0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=330086
@ -1,8 +1,13 @@
|
||||
# $NetBSD: Makefile,v 1.3 2009/04/14 22:15:26 lukem Exp $
|
||||
# $FreeBSD$
|
||||
|
||||
.include <src.opts.mk>
|
||||
|
||||
PROG= seq
|
||||
|
||||
LIBADD= m
|
||||
|
||||
HAS_TESTS=
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -76,16 +76,19 @@ static char *unescape(char *);
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c = 0, errflg = 0;
|
||||
int equalize = 0;
|
||||
double first = 1.0;
|
||||
double last = 0.0;
|
||||
double incr = 0.0;
|
||||
const char *sep, *term;
|
||||
struct lconv *locale;
|
||||
char *fmt = NULL;
|
||||
const char *sep = "\n";
|
||||
const char *term = NULL;
|
||||
char pad = ZERO;
|
||||
char pad, *fmt, *cur_print, *last_print;
|
||||
double first, last, incr, last_shown_value, cur, step;
|
||||
int c, errflg, equalize;
|
||||
|
||||
pad = ZERO;
|
||||
fmt = NULL;
|
||||
first = 1.0;
|
||||
last = incr = last_shown_value = 0.0;
|
||||
c = errflg = equalize = 0;
|
||||
sep = "\n";
|
||||
term = NULL;
|
||||
|
||||
/* Determine the locale's decimal point. */
|
||||
locale = localeconv();
|
||||
@ -169,17 +172,32 @@ main(int argc, char *argv[])
|
||||
} else
|
||||
fmt = generate_format(first, incr, last, equalize, pad);
|
||||
|
||||
if (incr > 0) {
|
||||
for (; first <= last; first += incr) {
|
||||
printf(fmt, first);
|
||||
fputs(sep, stdout);
|
||||
}
|
||||
} else {
|
||||
for (; first >= last; first += incr) {
|
||||
printf(fmt, first);
|
||||
fputs(sep, stdout);
|
||||
}
|
||||
for (step = 1, cur = first; incr > 0 ? cur <= last : cur >= last;
|
||||
cur = first + incr * step++) {
|
||||
printf(fmt, cur);
|
||||
fputs(sep, stdout);
|
||||
last_shown_value = cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Did we miss the last value of the range in the loop above?
|
||||
*
|
||||
* We might have, so check if the printable version of the last
|
||||
* computed value ('cur') and desired 'last' value are equal. If they
|
||||
* are equal after formatting truncation, but 'cur' and
|
||||
* 'last_shown_value' are not equal, it means the exit condition of the
|
||||
* loop held true due to a rounding error and we still need to print
|
||||
* 'last'.
|
||||
*/
|
||||
asprintf(&cur_print, fmt, cur);
|
||||
asprintf(&last_print, fmt, last);
|
||||
if (strcmp(cur_print, last_print) == 0 && cur != last_shown_value) {
|
||||
fputs(last_print, stdout);
|
||||
fputs(sep, stdout);
|
||||
}
|
||||
free(cur_print);
|
||||
free(last_print);
|
||||
|
||||
if (term != NULL)
|
||||
fputs(term, stdout);
|
||||
|
||||
|
7
usr.bin/seq/tests/Makefile
Normal file
7
usr.bin/seq/tests/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= tests
|
||||
|
||||
ATF_TESTS_SH= seq_test
|
||||
|
||||
.include <bsd.test.mk>
|
40
usr.bin/seq/tests/seq_test.sh
Executable file
40
usr.bin/seq/tests/seq_test.sh
Executable file
@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
|
||||
# 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 float_rounding
|
||||
float_rounding_head()
|
||||
{
|
||||
atf_set "descr" "Check for correct termination in the face of floating point rounding"
|
||||
}
|
||||
float_rounding_body()
|
||||
{
|
||||
atf_check -o inline:'1\n1.1\n1.2\n' seq 1 0.1 1.2
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case float_rounding
|
||||
}
|
Loading…
Reference in New Issue
Block a user