Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add round_to_even to floating point types #82273

Closed
wants to merge 3 commits into from

Conversation

dsprenkels
Copy link
Contributor

@dsprenkels dsprenkels commented Feb 18, 2021

When rounding floats that lie exactly inbetween two integers (i.e., every float that ends with .5), The default rounding behaviour in Rust is rounding away from 0. Sometimes however, it is desired to round to the closest even number instead (banker's rounding).
It is currently not very easy to use rounding to even numbers in Rust, although LLVM has an intrinsic that supports this.

This patch adds support for rounding using the round-to-even mode.

When does this matter?

It is often not very relevant how this edge case is handled. However, one example of how it could matter is when you are rounding floats in a list, and afterward computing the sum or average. For example:

0.5f64 + 1.5f64 + 2.5f64 + 3.5f64 // (real value) = 8.0
0.5f64.round() + 1.5f64.round() + 2.5f64.round() + 3.5f64.round() // = 10.0
0.5f64.round_to_even() + 1.5f64.round_to_even() + 2.5f64.round_to_even() + 3.5f64.round_to_even() // = 8.0

In this example you see how an unintended statistical bias was introduced, just because of how we handled the rounding of half-integral numbers.

Other langagues

Here is an overview of the default behaviour in some other languages.

Tie away from 0 Tie to even Tie towards +Inf
Rust X
C/C++ X
JavaScript X
Python X
Go X X [*]
Julia X
Java X
Haskell X
C# X
Matlab X

[*] Go has a separate Math.RoundToEven function which does rounding-to-even.
See JuliaLang/julia#8750 for a more complete (albeit somewhat outdated) survey.

Alternatives

  • Let the user implement this function themselves.
  • Change the default rounding behaviour of Rust to from tie-away-from-zero to tie-to-even, and add a .round_from_zero() function instead. This may actually not be a very weird idea. I myself was surprised that the default rounding behaviour in Rust was not tie-to-even, and when I look for example at the discussion in float rounding is slow #55107 it seems that I'm not the only one that expected it to behave differently. Obviously, because the documentation currently states promises that the behaviour is tie-away-from-zero, that would probably be a breaking change.

Related discussions

r? @m-ou-se

@rust-highfive
Copy link
Collaborator

Some changes occured to rustc_codegen_cranelift

cc @bjorn3

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 18, 2021
@m-ou-se m-ou-se added A-floating-point Area: Floating point numbers and arithmetic T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Feb 18, 2021
@rust-log-analyzer

This comment has been minimized.

Copy link
Member

@m-ou-se m-ou-se left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This looks like a function we should have.

Comment on lines +500 to +501
roundevenf32(flt) -> f32 => roundevenf,
roundevenf64(flt) -> f64 => roundeven,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this will cause calls to the roundeven and roundevenf libc functions. But those are non-standard, and not available on all libc implementations.

cc @bjorn3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cranelift doesn't have an instruction for this. If this ever becomes a problem, I can request one.

Comment on lines +576 to +577
ifn!("llvm.roundeven.f32", fn(t_f32) -> t_f32);
ifn!("llvm.roundeven.f64", fn(t_f64) -> t_f64);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nagisa Can you confirm it's okay to add these llvm intrinsics?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It is documented here. How available it is across different backends is a different question, however – a quick look suggests these lower to native instructions on aarch64 and x86 only (and maybe amdgpu). Everything else will lower to libm calls.

library/core/src/intrinsics.rs Outdated Show resolved Hide resolved
library/core/src/intrinsics.rs Outdated Show resolved Hide resolved
library/std/src/f32.rs Outdated Show resolved Hide resolved
library/std/src/f64/tests.rs Outdated Show resolved Hide resolved
library/std/src/f64.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job x86_64-gnu-llvm-9 failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
.................................................................................................... 9200/11468
.................................................................................................... 9300/11468
.................................................................................................... 9400/11468
..........................i......i.................................................................. 9500/11468
.................................................................iiiiiii..iiiiii.i.................. 9600/11468
.................................................................................................... 9800/11468
.................................................................................................... 9900/11468
.................................................................................................... 10000/11468
.................................................................................................... 10100/11468
---
Suite("src/test/assembly") not skipped for "bootstrap::test::Assembly" -- not in ["src/tools/tidy"]
Check compiletest suite=assembly mode=assembly (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 29 tests
iiiiiiiiiiiiiiiiiiiiiiiiiiiii

 finished in 0.069 seconds
Suite("src/test/incremental") not skipped for "bootstrap::test::Incremental" -- not in ["src/tools/tidy"]
Check compiletest suite=incremental mode=incremental (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
---
Suite("src/test/debuginfo") not skipped for "bootstrap::test::Debuginfo" -- not in ["src/tools/tidy"]
Check compiletest suite=debuginfo mode=debuginfo (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 116 tests
iiiiiiiiii.i.i..i..i..ii....i.i....ii..........iiii.........i.....i...i.......ii.i.ii.....iiii.....i 100/116
test result: ok. 78 passed; 0 failed; 38 ignored; 0 measured; 0 filtered out; finished in 2.35s

 finished in 2.415 seconds
Suite("src/test/ui-fulldeps") not skipped for "bootstrap::test::UiFullDeps" -- not in ["src/tools/tidy"]
---
Set({"library/std"}) not skipped for "bootstrap::test::Crate" -- not in ["src/tools/tidy"]
 finished in 2.598 seconds
Testing std stage1 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
   Compiling std v0.0.0 (/checkout/library/std)
error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-Wl,--eh-frame-hdr" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.0.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.1.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.10.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.11.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.12.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.13.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.14.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.15.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.2.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.3.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.4.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.5.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.6.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.7.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.8.rcgu.o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.9.rcgu.o" "-o" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-Wl,-O1" "-nodefaultlibs" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/release/deps" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/build/compiler_builtins-44a0a549c4d20832/out" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/librand-f206f162d5fcbf13.rlib" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/librand_chacha-48cc45927a747e37.rlib" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/libppv_lite86-91ea22edafcf05ed.rlib" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/librand_core-3eb5418979e33a4d.rlib" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/libgetrandom-52a3b20344a46072.rlib" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bdynamic" "-ltest-5c4e3aa847ac7832" "-Wl,--start-group" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-lstd-360a2d238031f7d1" "-Wl,--end-group" "-Wl,-Bstatic" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-a3426394929bdfab.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,-rpath,$ORIGIN/../lib"
  = note: /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.1.rcgu.o: In function `std::f64::tests::test_round_to_even':
          std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0xe): undefined reference to `llvm.roundeven.f64'
          std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0x43): undefined reference to `llvm.roundeven.f64'
          std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0x78): undefined reference to `llvm.roundeven.f64'
          std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0xad): undefined reference to `llvm.roundeven.f64'
          std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0xe2): undefined reference to `llvm.roundeven.f64'
          /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.1.rcgu.o:std.af8qn5ha-cgu.1:(.text._ZN3std3f645tests18test_round_to_even17h7bb5de47ee68f75cE+0x113): more undefined references to `llvm.roundeven.f64' follow
          /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.15.rcgu.o: In function `std::f32::tests::test_round_to_even':
          std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0xe): undefined reference to `llvm.roundeven.f32'
          std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0x42): undefined reference to `llvm.roundeven.f32'
          std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0x76): undefined reference to `llvm.roundeven.f32'
          std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0xaa): undefined reference to `llvm.roundeven.f32'
          std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0xde): undefined reference to `llvm.roundeven.f32'
          /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/std-c499aaf4f8b73525.std.af8qn5ha-cgu.15.rcgu.o:std.af8qn5ha-cgu.15:(.text._ZN3std3f325tests18test_round_to_even17h60b787ccaecb27e8E+0x10d): more undefined references to `llvm.roundeven.f32' follow
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `std`
error: could not compile `std`

To learn more, run the command again with --verbose.


command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "16" "--release" "--locked" "--color" "always" "--features" "panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/checkout/library/test/Cargo.toml" "-p" "std" "--" "--quiet"


failed to run: /checkout/obj/build/bootstrap/debug/bootstrap --stage 2 test --exclude src/tools/tidy
Build completed unsuccessfully in 0:23:23

@tspiteri
Copy link
Contributor

I find the name round_to_even misleading, as it is easy to misunderstand that name to mean the function rounds 1.3 to 2. That is, the name can mislead that the function will not round to an odd number in any case. I prefer round_even as it is shorter and less misleading; I cannot think of one reason why round_to_even would be better. (I've used both round_ties_to_even and round_even myself, but I now think that round_even is clear enough and the longer name is unnecessary.)

@m-ou-se
Copy link
Member

m-ou-se commented Mar 3, 2021

@dsprenkels Looks like this is failing to compile on llvm 9 at least. Can you investigate? (I'm guessing the intrinsics are simply not available on that version?)

@m-ou-se m-ou-se added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 3, 2021
@dsprenkels
Copy link
Contributor Author

The support for roundeven was added last year in LLVM.
I recompiled rust with the different rustc/... llvm versions. The first version for which this patch compiles successfully is rustc/11.0-2020-08-20.

Does this mean that we have to wait a while until we can merge this?

@dsprenkels
Copy link
Contributor Author

I did not realize that LLVM introduced this intrinsic so recently. I am presuming that, all this time, rust have just used the round-away-from-zero semantics, because that was the default behaviour of LLVM.

Would actually maybe be an interest in updating the default behaviour to round-to-even?

@m-ou-se
Copy link
Member

m-ou-se commented Mar 4, 2021

The support for roundeven was added last year in LLVM.
I recompiled rust with the different rustc/... llvm versions. The first version for which this patch compiles successfully is rustc/11.0-2020-08-20.

Does this mean that we have to wait a while until we can merge this?

@nagisa Can we only use intrinsics that have been available since llvm 9, or is there a mechanism to have some kind of fallback for older llvm versions?

@dsprenkels As an alternative you could directly call the roundeven libc function. (See the cmath modules in library/std/src/sys.) But you'd have to verify first that this function is available everywhere.

@workingjubilee
Copy link
Member

workingjubilee commented Mar 8, 2021

It is very often the case that compiling to an LLVM "intrinsic" becomes manifested as a call to libm anyways, as already noted.
I will note that this is actually the default behavior specified by IEEE754 for f32s and f64s (though it is not the default behavior of C or C++ or LLVM), so the suggestion of adopting it as the default rounding is not as odd as it might seem. However, fn round already documents the C-like behavior, so that would still be a break with the publicly specified API.

@dsprenkels
Copy link
Contributor Author

Hey all,

The reason why this issue is stale for me is related to the comment by @workingjubilee.

After learning that the round-to-even behaviour should be the default behaviour, I would like to see it changed in the fn round, and probably close this PR.

However, this should probably be done by writing an RFC, as (as mentioned) it changes the currently documented behaviour. I have not done this before, so it may take some time until I have motivation to do this.

If anybody is interested in collaborating with me on this, feel free to ping me. :)

@workingjubilee
Copy link
Member

Oh no, I didn't mean to take the wind out of your sails!

@dsprenkels
Copy link
Contributor Author

@workingjubilee Don't worry about it. I had already decided this before the comment. :)

@crlf0710
Copy link
Member

crlf0710 commented Apr 9, 2021

@dsprenkels Ping from triage, anything new on this? If you want to chat about it maybe just creating a topic on Zulip t-lang stream will suffice.

@crlf0710 crlf0710 added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 9, 2021
@dsprenkels
Copy link
Contributor Author

@crlf0710 The current status is that I would actually like to not merge this PR, but rather want to update language to uses round-to-even by default instead. This would obviously need an RFC. That's an involved process (and I don't really know exactly what the requirements are, so I'm putting that off at the moment. Any help is appreciated! :)

Closing this PR for now.

@dsprenkels dsprenkels closed this Apr 9, 2021
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 7, 2023
…=pnkfelix,m-ou-se,scottmcm

Add `round_ties_even` to `f32` and `f64`

Tracking issue: rust-lang#96710

Redux of rust-lang#82273. See also rust-lang#55107

Adds a new method, `round_ties_even`, to `f32` and `f64`, that rounds the float to the nearest integer , rounding halfway cases to the number with an even least significant bit. Uses the `roundeven` LLVM intrinsic to do this.

Of the five IEEE 754 rounding modes, this is the only one that doesn't already have a round-to-integer function exposed by Rust (others are `round`, `floor`, `ceil`, and `trunc`).  Ties-to-even is also the rounding mode used for int-to-float and float-to-float `as` casts, as well as float arithmentic operations. So not having an explicit rounding method for it seems like an oversight.

Bikeshed: this PR currently uses `round_ties_even` for the name of the method. But maybe `round_ties_to_even` is better, or `round_even`, or `round_to_even`?
bjorn3 pushed a commit to bjorn3/rust that referenced this pull request Mar 15, 2023
…=pnkfelix,m-ou-se,scottmcm

Add `round_ties_even` to `f32` and `f64`

Tracking issue: rust-lang#96710

Redux of rust-lang#82273. See also rust-lang#55107

Adds a new method, `round_ties_even`, to `f32` and `f64`, that rounds the float to the nearest integer , rounding halfway cases to the number with an even least significant bit. Uses the `roundeven` LLVM intrinsic to do this.

Of the five IEEE 754 rounding modes, this is the only one that doesn't already have a round-to-integer function exposed by Rust (others are `round`, `floor`, `ceil`, and `trunc`).  Ties-to-even is also the rounding mode used for int-to-float and float-to-float `as` casts, as well as float arithmentic operations. So not having an explicit rounding method for it seems like an oversight.

Bikeshed: this PR currently uses `round_ties_even` for the name of the method. But maybe `round_ties_to_even` is better, or `round_even`, or `round_to_even`?
antoyo pushed a commit to antoyo/rust that referenced this pull request Jun 19, 2023
…=pnkfelix,m-ou-se,scottmcm

Add `round_ties_even` to `f32` and `f64`

Tracking issue: rust-lang#96710

Redux of rust-lang#82273. See also rust-lang#55107

Adds a new method, `round_ties_even`, to `f32` and `f64`, that rounds the float to the nearest integer , rounding halfway cases to the number with an even least significant bit. Uses the `roundeven` LLVM intrinsic to do this.

Of the five IEEE 754 rounding modes, this is the only one that doesn't already have a round-to-integer function exposed by Rust (others are `round`, `floor`, `ceil`, and `trunc`).  Ties-to-even is also the rounding mode used for int-to-float and float-to-float `as` casts, as well as float arithmentic operations. So not having an explicit rounding method for it seems like an oversight.

Bikeshed: this PR currently uses `round_ties_even` for the name of the method. But maybe `round_ties_to_even` is better, or `round_even`, or `round_to_even`?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-floating-point Area: Floating point numbers and arithmetic S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants